Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laelaps_diag.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Program: laelaps_diag
6 //
7 // File: laelaps_diag.cxx
8 //
9 /*! \file
10  *
11  * \brief Perform Laelaps hardware and software interface diagnostics.
12  *
13  * This C++ version uses the liblaelaps library, so both it and the hardware
14  * are tested.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \par Copyright
19  * \h_copy 2015-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 /*
24  * @EulaBegin@
25  *
26  * Unless otherwise stated explicitly, all materials contained are copyrighted
27  * and may not be used without RoadNarrows LLC's written consent,
28  * except as provided in these terms and conditions or in the copyright
29  * notice (documents and software) or other proprietary notice provided with
30  * the relevant materials.
31  *
32  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
33  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
34  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
35  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
36  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
37  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
40  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
42  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
43  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
44  *
45  * @EulaEnd@
46  */
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <unistd.h>
50 #include <string.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <errno.h>
54 
55 #include <string>
56 #include <vector>
57 
58 #include "rnr/rnrconfig.h"
59 #include "rnr/log.h"
60 #include "rnr/opts.h"
61 #include "rnr/pkg.h"
62 
63 // common
64 #include "Laelaps/laelaps.h"
65 #include "Laelaps/laeUtils.h"
66 #include "Laelaps/laeDesc.h"
67 #include "Laelaps/laeXmlCfg.h"
68 
69 // hardware
70 #include "Laelaps/laeSysDev.h"
71 #include "Laelaps/RoboClaw.h"
72 #include "Laelaps/laeMotor.h"
73 #include "Laelaps/laeI2C.h"
74 #include "Laelaps/laeI2CMux.h"
75 
76 #include "version.h"
77 
78 #include "laelaps_diag.h"
79 
80 using namespace std;
81 using namespace laelaps;
82 
83 
84 //------------------------------------------------------------------------------
85 // Private Interface
86 //------------------------------------------------------------------------------
87 
88 /*!
89  * \ingroup apps
90  * \defgroup laelaps_diag laelaps_diag
91  * \{
92  */
93 
94 #define APP_EC_OK 0 ///< success exit code
95 #define APP_EC_ARGS 2 ///< command-line options/arguments error exit code
96 #define APP_EC_EXEC 4 ///< execution exit code
97 
98 static char *Argv0; ///< the command
99 static bool_t OptsMotion = false; ///< motion option
100 static bool_t OptsAnyKey = false; ///< user presses anykey to stop option
101 
102 /*! \brief Available diagnostics. */
103 static const char *DiagnosticsAvail[] =
104 {
105  "product", "cpu", "motors", "watchdog", "tof", "imu", "cam", "batt"
106 };
107 
108 static vector<string> DiagnosticsToRun; ///< diagnostics to run
109 
110 /*!
111  * \brief Program information.
112  */
113 static OptsPgmInfo_T PgmInfo =
114 {
115  // usage_args
116  "DIAG [DIAG ...]",
117 
118  // synopsis
119  "Run Laelaps diagnostics.",
120 
121  // long_desc =
122  "The %P command executes Laelaps subsystem diagnostics. "
123  "The Laelaps embedded software interfaces are used to test the subsystems."
124  "\n\n"
125  "DIAG: Diagnostic to run. One of:\n"
126  " all - Run all dignostics.\n"
127  " batt - Run battery dignostics.\n"
128  " cam - Run camera diagnotics.\n"
129  " cpu - Run main CPU diagnotics.\n"
130  " imu - Run Inertia Measurement Unit diagnostics.\n"
131  " motors - Run motors diagnotics.\n"
132  " product - Run Laelaps product diagnotics.\n"
133  " tof - Run time-of-flight sensors diagnostics.\n"
134  " watchdog - Run Arduino watchdog sub-processor diagnostics.\n\n"
135  "The ROS node laelaps_control cannot be running while using this tool.",
136 
137  // diagnostics
138  NULL
139 };
140 
141 /*!
142  * \brief Command line options information.
143  */
144 static OptsInfo_T OptsInfo[] =
145 {
146  // --motion
147  {
148  "motion", // long_opt
149  OPTS_NO_SHORT, // short_opt
150  no_argument, // has_arg
151  true, // has_default
152  &OptsMotion, // opt_addr
153  OptsCvtArgBool, // fn_cvt
154  OptsFmtBool, // fn_fmt
155  NULL, // arg_name
156  // opt desc
157  "Run diagnositcs that causes the Laelaps to move."
158  },
159 
160  // --anykey
161  {
162  "prompt", // long_opt
163  OPTS_NO_SHORT, // short_opt
164  no_argument, // has_arg
165  true, // has_default
166  &OptsAnyKey, // opt_addr
167  OptsCvtArgBool, // fn_cvt
168  OptsFmtBool, // fn_fmt
169  NULL, // arg_name
170  // opt desc
171  "For each diagnositc, the user presses any keyboard key to progress to"
172  " the next diagnostic. Only valid for batt, imu, tof diagnostics."
173  },
174 
175 
176  {NULL, }
177 };
178 
179 
180 static DiagStats prelims()
181 {
182  bool bUsesI2C = false;
183  bool bUsesWd = false;
184  DiagStats statsTotal;
185  LaeXmlCfg xml;
186 
187  printHdr("Diagnostic Prelims");
188 
189  ++statsTotal.testCnt;
190 
191  if( findProc("laelaps_control/laelaps_control") > 1 )
192  {
193  printTestResult(FatalTag,
194  "The laelaps_control ROS node cannot be running. "
195  "Stop with either:\n"
196  " - 'laelaps_init' GUI\n"
197  " - 'sudo laelaps_service stop' utility\n"
198  " - 'sudo service laelaps_control stop' system init script.");
199  statsTotal.fatal = true;
200  }
201  else
202  {
203  printTestResult(PassTag, "The laelaps_control ROS node is not running.");
204  ++statsTotal.passCnt;
205  }
206 
207  ++statsTotal.testCnt;
208 
209  //
210  // Get robot description (and version)
211  //
212  if( !statsTotal.fatal )
213  {
214  if( xml.load(RobotDesc, LaeSysCfgPath, LaeEtcCfg) < 0 )
215  {
216  printTestResult(FatalTag, "Loading XML file '%s' failed.", LaeEtcCfg);
217  statsTotal.fatal = true;
218  return statsTotal;
219  }
220  else
221  {
222  RobotDesc.markAsDescribed();
223  printTestResult(PassTag, "Robot v%s description from '%s' loaded.",
224  RobotDesc.getProdHwVerString().c_str(), LaeEtcCfg);
225  ++statsTotal.passCnt;
226  }
227  }
228 
229  //
230  // Laelaps diagnostics that use shared resources.
231  //
232  for(size_t i = 0; i < DiagnosticsToRun.size(); ++i)
233  {
234  if( (DiagnosticsToRun[i] == "watchdog") ||
235  (DiagnosticsToRun[i] == "motors") ||
236  (DiagnosticsToRun[i] == "batt") )
237  {
238  bUsesI2C = true;
239  bUsesWd = true;
240  }
241  else if( (DiagnosticsToRun[i] == "tof") )
242  {
243  bUsesI2C = true;
244  }
245  }
246 
247  if( !statsTotal.fatal && bUsesI2C )
248  {
249  ++statsTotal.testCnt;
250  if( i2cTryOpen(I2CBus, LaeI2CAddrWd) < 0 )
251  {
252  printTestResult(FailTag, "Open I2C bus failed.");
253  statsTotal.fatal = true;
254  }
255  else
256  {
257  printTestResult(PassTag, "I2C bus opened.");
258  ++statsTotal.passCnt;
259  }
260  }
261 
262  if( !statsTotal.fatal && bUsesWd )
263  {
264  uint_t uFwVer;
265 
266  ++statsTotal.testCnt;
267  if( WatchDog.cmdGetFwVersion(uFwVer) < 0 )
268  {
269  printTestResult(FailTag, "WatchDog: Failed to get firmware version.");
270  }
271  else
272  {
273  WatchDog.sync();
274  printTestResult(PassTag, "Connected to WatchDog sub-processor, fwver=%u.",
275  uFwVer);
276  ++statsTotal.passCnt;
277  }
278  }
279 
280  ++statsTotal.testCnt;
281  printTestResult(PassTag, "Preliminaries completed.");
282  ++statsTotal.passCnt;
283 
284  printf("\n");
285  printTotals(statsTotal);
286 
287  return statsTotal;
288 }
289 
290 /*!
291  * \brief Main initialization.
292  *
293  * \param argc Command-line argument count.
294  * \param argv Command-line argument list.
295  *
296  * \par Exits:
297  * Program terminates on conversion error.
298  */
299 static void mainInit(int argc, char *argv[])
300 {
301  string strArg;
302  bool bFound;
303 
304  // name of this process
305  Argv0 = basename(argv[0]);
306 
307  // parse input options
308  argv = OptsGet(Argv0, &PkgInfo, &PgmInfo, OptsInfo, true, &argc, argv);
309 
310  if( argc == 0 )
311  {
312  fprintf(stderr, "%s: Error: No diagnostics specified.\n", Argv0);
313  fprintf(stderr, "Try '%s --help' for more information.\n", Argv0);
314  exit(APP_EC_ARGS);
315  }
316 
317  for(int i = 0; i < argc; ++i)
318  {
319 
320  strArg = argv[i];
321 
322  if( strArg == "all" )
323  {
324  DiagnosticsToRun.clear();
325  for(size_t j = 0; j < arraysize(DiagnosticsAvail); ++j)
326  {
327  DiagnosticsToRun.push_back(DiagnosticsAvail[j]);
328  }
329  break;
330  }
331 
332  bFound = false;
333 
334  for(size_t j = 0; j < arraysize(DiagnosticsAvail); ++j)
335  {
336  if( strArg == DiagnosticsAvail[j] )
337  {
338  DiagnosticsToRun.push_back(DiagnosticsAvail[j]);
339  bFound = true;
340  break;
341  }
342  }
343 
344  if( !bFound )
345  {
346  fprintf(stderr, "%s: Warning: Unknown diganostic %s - ignoring.\n",
347  Argv0, strArg.c_str());
348  }
349  }
350 
351  if( DiagnosticsToRun.empty() )
352  {
353  fprintf(stderr, "%s: Error: No valid diagnostics to run.\n", Argv0);
354  fprintf(stderr, "Try '%s --help' for more information.\n", Argv0);
355  exit(APP_EC_ARGS);
356  }
357 }
358 
359 
360 //------------------------------------------------------------------------------
361 // Public Interface
362 //------------------------------------------------------------------------------
363 
364 //
365 // Shared interfaces used by diagnostics
366 //
367 LaeDesc RobotDesc;
368 LaeI2C I2CBus;
369 LaeI2CMux I2CMux(I2CBus);
370 LaeWd WatchDog(I2CBus);
371 
372 /*!
373  * \brief Main.
374  *
375  * \param argc Command-line argument count.
376  * \param argv Command-line argument list.
377  *
378  * \return Returns 0 on succes, non-zero on failure.
379  */
380 int main(int argc, char* argv[])
381 {
382  string strDiag;
383  DiagStats statsGrandTotal;
384 
385  mainInit(argc, argv);
386 
387  setTags(LOG_IN_COLOR());
388 
389  statsGrandTotal = prelims();
390 
391  for(size_t i = 0; i < DiagnosticsToRun.size(); ++i)
392  {
393  if( statsGrandTotal.fatal )
394  {
395  break;
396  }
397 
398  strDiag = DiagnosticsToRun[i];
399 
400  if( strDiag == "product" )
401  {
402  statsGrandTotal += runProductDiagnostics();
403  }
404  else if( strDiag == "cpu" )
405  {
406  statsGrandTotal += runCpuDiagnostics();
407  }
408  else if( strDiag == "motors" )
409  {
410  statsGrandTotal += runMotorsDiagnostics(OptsMotion);
411  }
412  else if( strDiag == "cam" )
413  {
414  statsGrandTotal += runCamDiagnostics();
415  }
416  else if( strDiag == "tof" )
417  {
418  statsGrandTotal += runToFDiagnostics(OptsAnyKey);
419  }
420  else if( strDiag == "imu" )
421  {
422  statsGrandTotal += runImuDiagnostics(OptsAnyKey);
423  }
424  else if( strDiag == "watchdog" )
425  {
426  statsGrandTotal += runWatchDogDiagnostics();
427  }
428  else if( strDiag == "batt" )
429  {
430  statsGrandTotal += runBatteryDiagnostics(OptsAnyKey);
431  }
432  }
433 
434  printGrandTotals(statsGrandTotal);
435 
436  return APP_EC_OK;
437 }
438 
439 /*!
440  * \}
441  */
Diagnotics header file.
#define APP_EC_ARGS
command-line options/arguments error exit code
Laelaps robotic mobile platform full description class.
Definition: laeDesc.h:451
control register mask
Definition: laeI2CMux.h:100
#define APP_EC_OK
success exit code
RoboClaw motor controller class interface.
static char * Argv0
the command
Laelaps PCA9548A I2C multiplexer switch interface.
static OptsPgmInfo_T PgmInfo
Program information.
Laelaps I2C class interface.
static bool_t OptsAnyKey
user presses anykey to stop option
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
Laelaps robotic base mobile platform description class interface.
static const PkgInfo_T PkgInfo
Definition: version.h:45
Simple diagnostics statistics class.
Definition: laelaps_diag.h:106
Laelaps common utilities.
static OptsInfo_T OptsInfo[]
Command line options information.
static bool_t OptsMotion
motion option
int markAsDescribed()
Mark <b><i>Laelaps</i></b> hardware as fully described.
Definition: laeDesc.cxx:672
Package version information.
Laelaps system devices.
int main(int argc, char *argv[])
Main.
virtual int cmdGetFwVersion(uint_t &uVerNum)
Get the firmware version command.
Definition: laeWd.cxx:263
Laelaps motors, encoder, and controllers hardware abstraction interfaces.
static const char * DiagnosticsAvail[]
Available diagnostics.
static void mainInit(int argc, char *argv[])
Main initialization.
<b><i>Laelaps</i></b> XML configuration class interface.
static vector< string > DiagnosticsToRun
diagnostics to run
virtual int load(LaeDesc &desc, const std::string &strSearchPath, const std::string &strXmlFileName, bool bAllInstances=false)
Load XML file into DOM and set the <b><i>Laelaps</i></b> description.
Definition: laeXmlCfg.cxx:65
std::string getProdHwVerString() const
Get this robot&#39;s hardware version string.
Definition: laeDesc.h:598
int i2cTryOpen(LaeI2C &i2cBus, uint_t addr)
Try to open a series of I2C devices to fine the required endpoint.
Definition: laeI2C.cxx:67
virtual void sync()
Synchronize watchdog state with subprocessor state.
Definition: laeWd.cxx:142
Top-level package include file.
LaeXmlCfg <b><i>Laelaps</i></b> XML configuration class.
Definition: laeXmlCfg.h:71