Dynamixel  2.9.5
RoadNarrows Robotics Dynamixel Package
dynashell.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Dynamixel
4 //
5 // Program: dynashell
6 //
7 // File: dynashell.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2015-01-12 10:56:06 -0700 (Mon, 12 Jan 2015) $
12  * $Rev: 3845 $
13  *
14  * \brief The Dynamixel Shell Class Execution Control and Support Functions.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \copyright
19  * \h_copy 2011-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 
50 #include <stdio.h>
51 #include <stdarg.h>
52 
53 #include <cstring>
54 #include <iostream>
55 #include <fstream>
56 #include <map>
57 #include <string>
58 #include <vector>
59 
60 #include "rnr/rnrconfig.h"
61 #include "rnr/log.h"
62 
63 #include "Dynamixel/Dynamixel.h"
64 #include "Dynamixel/DynaComm.h"
67 #include "Dynamixel/DynaServo.h"
68 
69 #include "dynashell.h"
70 #include "dynashell_cmd.h"
71 #include "dynashell_readline.h"
72 #include "dynashell_util.h"
73 
74 using namespace std;
75 
76 
77 // ----------------------------------------------------------------------------
78 // Private Inteface
79 // ----------------------------------------------------------------------------
80 
81 #define SH_RC_OK 0 ///< shell input ok
82 #define SH_RC_OK_NOEXEC 1 ///< shell input ok, but don't execute command
83 #define SH_RC_EOF 2 ///< end of file
84 #define SH_RC_SYS_ERROR 3 ///< system error on shell input
85 #define SH_RC_INTR 4 ///< shell interrupt
86 
87 
88 // ----------------------------------------------------------------------------
89 // DynaShell Class
90 // ----------------------------------------------------------------------------
91 
92 /*!
93  * \breif DynaShell default constructor.
94  */
96  : m_sShellName("dynashell"),
97  m_sPS1("dynashell> "),
98  m_sPS2(": "),
99  m_ReadLine(m_sShellName)
100 {
101  m_bIsInteractive = true;
102  m_bXTrace = false;
103  m_bRun = true;
104  m_bNoExec = false;
105  m_bSilent = false;
106  m_pDynaComm = NULL;
107  m_pDynaChain = NULL;
108  m_pDynaBgThread = NULL;
109  m_pRecording = NULL;
110  m_rc = DYNA_OK;
111 
112  InputInit();
113 };
114 
115 /*!
116  * \breif DynaShell destructor.
117  */
119 {
120  if( m_pDynaBgThread != NULL )
121  {
123  delete m_pDynaBgThread;
124  }
125 
126  if( m_pDynaChain != NULL )
127  {
128  delete m_pDynaChain;
129  }
130 
131  if( m_pDynaComm != NULL )
132  {
133  delete m_pDynaComm;
134  }
135 
136  if( m_pRecording != NULL )
137  {
138  delete m_pRecording;
139  }
140 
141  ScriptFlush();
142 
144 }
145 
146 /*!
147  * \brief Publish new command map to shell.
148  *
149  * The new map is inserted into the map of root commands. The map is kept
150  * in lexicogrphahical increasing order.
151  *
152  * \note Published commands currently only support two levels. However,
153  * this can be readily extended given some command separator convention.
154  *
155  * \param sParent Parent command. Set to NULL or "" to publish the command at
156  * the root(0) level.
157  * \param strBrief Map brief description.
158  */
159 void DynaShell::PublishMap(const char *sParent, const string &strBrief)
160 {
161  string strRegEx;
162  int nUid;
163 
164  // publish only if map does not exist
165  if( m_mapCmds.find(sParent) == m_mapCmds.end() )
166  {
167  m_mapCmds[sParent] = new ShCmdMap;
168  m_mapCmds[sParent].SetMapBrief(strBrief);
169 
170  strRegEx = "^";
171  strRegEx += sParent;
172  strRegEx += "$";
173 
174  nUid = m_ReadLine.RegisterGenerator(strRegEx, CmdListGeneratorWrap, this);
175  }
176 }
177 
178 /*!
179  * \brief Publish new command to shell.
180  *
181  * The new command is inserted into the map of commands. The map is kept
182  * in lexicogrphahical increasing order.
183  *
184  * \note Published commands currently only support two levels. However,
185  * this can be readily extended given some command separator convention.
186  *
187  * \param sParent Parent command. Set to NULL or "" to publish the command at
188  * the root(0) level.
189  * \param pNewCmd Allocated new command. The shell owns the command and will
190  * delete it when the shell is destroyed.
191  */
192 void DynaShell::PublishCommand(const char *sParent, DynaShellCmd *pNewCmd)
193 {
194  const char *sCmdName = pNewCmd->GetCmdName();
195  string strRegEx;
196  int nUid;
197 
198  // new root command
199  if( (sParent == NULL) || (*sParent == 0) )
200  {
201  pNewCmd->SetPublishedInfo(0);
202  m_mapCmds[sCmdName] = pNewCmd;
203  m_mapCmds[sCmdName].SetMapBrief(pNewCmd->GetCmdHelpBrief());
204  }
205 
206  // new command published to existing root group
207  else if( m_mapCmds.find(sParent) != m_mapCmds.end() )
208  {
209  pNewCmd->SetPublishedInfo(1, sParent);
210 
211  (*m_mapCmds[sParent].GetMap())[sCmdName] = pNewCmd;
212  (*m_mapCmds[sParent].GetMap())[sCmdName].SetMapBrief(
213  pNewCmd->GetCmdHelpBrief());
214  }
215 
216  // new command published to new root group
217  else
218  {
219  pNewCmd->SetPublishedInfo(1, sParent);
220 
221  m_mapCmds[sParent] = new ShCmdMap;
222 
223  (*m_mapCmds[sParent].GetMap())[sCmdName] = pNewCmd;
224 
225  strRegEx = "^";
226  strRegEx += sParent;
227  strRegEx += "$";
228 
229  nUid = m_ReadLine.RegisterGenerator(strRegEx, CmdListGeneratorWrap, this);
230  }
231 
232  // register new command for readline tab-completion
233  strRegEx = pNewCmd->GetPublishedName();
234  strRegEx += ".*";
235 
236  nUid = m_ReadLine.RegisterGenerator(strRegEx, CmdSpecGeneratorWrap, this);
237 
238  if( nUid != ReadLine::NOT_REG )
239  {
240  m_mapTabGen[nUid] = pNewCmd;
241  }
242 }
243 
244 /*!
245  * \brief Delete all published commands in map.
246  *
247  * \param cmdMap Map of published commands.
248  */
250 {
251  ShCmdMap::iterator iter;
252 
253  m_mapTabGen.clear();
254 
255  for(iter=cmdMap.begin(); iter!=cmdMap.end(); iter++)
256  {
257  if( iter->second.IsCmd() )
258  {
259  delete iter->second.GetCmd();
260  }
261  else if( iter->second.IsMap() )
262  {
263  DeleteCommands(*iter->second.GetMap());
264  }
265  }
266 }
267 
268 /*!
269  * \brief Initialize input.
270  */
272 {
274 }
275 
276 /*!
277  * \brief Command list generator wrapper function.
278  *
279  * \param nUid ReadLine registered unique id.
280  * \param sText Text string to complete as a command.
281  * \param uTextLen Length of text.
282  * \param nState Generator state. State lets us know whether to start from
283  * scratch. If state == START(0), then we start at the top of
284  * the command list.
285  * \param sContext Parent command path.
286  * \param pArg Pointer to this shell instance.
287  *
288  * \return
289  * On command partial matching, returns allocated full command name string.\n
290  * If no matches found, return NULL.
291  */
293  const char *sText,
294  size_t uTextLen,
295  int nState,
296  const char *sContext,
297  void *pArg)
298 {
299  DynaShell *pShell = (DynaShell *)pArg;
300 
301  return pShell->CmdListGenerator(nUid, sText, uTextLen, nState, sContext);
302 }
303 
304 /*!
305  * \brief Command list generator function.
306  *
307  * \param nUid ReadLine registered unique id.
308  * \param sText Text string to complete as a command.
309  * \param uTextLen Length of text.
310  * \param nState Generator state. State lets us know whether to start from
311  * scratch. If state == START(0), then we start at the top of
312  * the command list.
313  * \param sContext Parent command path.
314  *
315  * \return
316  * On command partial matching, returns allocated full command name string.\n
317  * If no matches found, return NULL.
318  */
320  const char *sText,
321  size_t uTextLen,
322  int nState,
323  const char *sContext)
324 {
325  static ShCmdMap *pMap; // command map
326  static ShCmdMap::iterator iterCmd; // command map iterator
327 
328  const char *sCmdName; // command name
329 
330  //
331  // New command to complete - initialize.
332  //
333  if( nState == ReadLine::FIRST )
334  {
335  pMap = NULL;
336  iterCmd = m_mapCmds.end();
337 
338  // root commands
339  if( (sContext == NULL) || (*sContext == 0) )
340  {
341  pMap = &m_mapCmds;
342  iterCmd = pMap->begin();
343  }
344 
345  // level 1 commands
346  else
347  {
348  iterCmd = m_mapCmds.find(sContext);
349  if( iterCmd != m_mapCmds.end() )
350  {
351  pMap = iterCmd->second.GetMap();
352  if( pMap != NULL )
353  {
354  iterCmd = pMap->begin();
355  }
356  }
357  }
358  }
359 
360  // return the next command which partially matches text
361  while( (pMap != NULL) && (iterCmd != pMap->end()) )
362  {
363  sCmdName = iterCmd->first;
364  iterCmd++;
365  if( !strncmp(sCmdName, sText, uTextLen) )
366  {
367  return ReadLine::dupstr(sCmdName);
368  }
369  }
370 
371  // no matches
372  return NULL;
373 }
374 
375 /*!
376  * \brief Command spec generator wrapper function.
377  *
378  * \param nUid ReadLine registered unique id.
379  * \param sText Text string to complete as a command.
380  * \param uTextLen Length of text.
381  * \param nState Generator state. State lets us know whether to start from
382  * scratch. If state == START(0), then we start at the top of
383  * the command list.
384  * \param sContext Parent command path.
385  * \param pArg Pointer to this shell instance.
386  *
387  * \return
388  * On command partial matching, returns allocated full command name string.\n
389  * If no matches found, return NULL.
390  */
392  const char *sText,
393  size_t uTextLen,
394  int nState,
395  const char *sContext,
396  void *pArg)
397 {
398  DynaShell *pShell = (DynaShell *)pArg;
399 
400  return pShell->CmdSpecGenerator(nUid, sText, uTextLen, nState, sContext);
401 }
402 
403 /*!
404  * \brief Command spec generator function.
405  *
406  * \param nUid ReadLine registered unique id.
407  * \param sText Text string to complete as a command.
408  * \param uTextLen Length of text.
409  * \param nState Generator state. State lets us know whether to start from
410  * scratch. If state == START(0), then we start at the top of
411  * the command list.
412  * \param sContext Parent command path.
413  *
414  * \return
415  * On command partial matching, returns allocated full command name string.\n
416  * If no matches found, return NULL.
417  */
419  const char *sText,
420  size_t uTextLen,
421  int nState,
422  const char *sContext)
423 {
424  MapTabCompleteGen::iterator pos;
425 
426  if( (pos = m_mapTabGen.find(nUid)) != m_mapTabGen.end() )
427  {
428  return pos->second->TabCompletion(*this, sText, uTextLen, nState, sContext);
429  }
430 
431  // no matches
432  else
433  {
434  return NULL;
435  }
436 }
437 
438 /*!
439  * \brief Add command line to history.
440  *
441  * \param sLine Null-terminated line string.
442  */
443 void DynaShell::AddToHistory(char *sLine)
444 {
445  if( IsExecStmt(sLine) && m_bIsInteractive && isatty(fileno(InputFp())) )
446  {
447  m_ReadLine.AddToHistory(sLine);
448  }
449 }
450 
451 /*!
452  * \brief Get one input line from file stream.
453  *
454  * Characters will be read from the input of the given file pointer using the
455  * prompt string to prompt the user. If the prompt is NULL or an empty string
456  * then no prompt is issued. The line returned is allocated, so the caller must
457  * free it when finished.
458  *
459  * If the readline free library is available, then command line editing is
460  * supported for terminal input.
461  *
462  * \todo TODO add '\\' parsing for line continuation.
463  *
464  * \return If no errors occurred and EOF is not encountered, an allocated,
465  * null-terminated line buffer is return. If the input only contained
466  * non-executable characters (white-space and comments), then the line buffer
467  * will be empty. On EOF or errors, NULL is returned.
468  */
470 {
471  static const char *sPrompt = m_sPS1;
472 
473  char *sLine = NULL;
474 
475  //
476  // Get malloc'd line of input (fragment)
477  //
478  if( isatty(fileno(InputFp())) )
479  {
480  sLine = m_ReadLine.iReadLine(sPrompt);
481  }
482  else
483  {
484  sLine = m_ReadLine.fReadLine(InputFp(), NULL);
486  }
487 
488  // EOF or I/O error
489  if( sLine == NULL )
490  {
491  return NULL;
492  }
493 
494  return sLine;
495 }
496 
497 /*!
498  * \brief Get one, non-empty input line.
499  *
500  * \return
501  * On success, returns SH_RC_OK).\n
502  * On end of file, returns SH_RC_EOF.\n
503  * On system error, returns SH_RC_SYS_ERROR.
504  */
506 {
507  char *sLine;
508 
509  m_sInputLine = NULL;
510 
511  while( true )
512  {
513  // get one logical line of input
514  sLine = GetInputLine();
515 
516  // eof or system error
517  if( sLine == NULL )
518  {
519  if( IsEOF() )
520  {
521  return SH_RC_EOF;
522  }
523  else
524  {
525  Error("%s.", strerror(errno));
526  return SH_RC_SYS_ERROR;
527  }
528  }
529 
530  // skip leading whitespace
531  while(*sLine && isspace(*sLine))
532  {
533  sLine++;
534  }
535 
536  if( IsExecStmt(sLine) )
537  {
538  // add non-blnak input line to command history
539  AddToHistory(sLine);
540 
541  // trace script
542  if( IsExecStmt(sLine) && (m_bIsInteractive || m_bXTrace) )
543  {
544  ScriptTrace(sLine);
545  }
546 
547  m_sInputLine = sLine;
548 
550  }
551  else
552  {
553  return SH_RC_OK_NOEXEC;
554  }
555  }
556 }
557 
558 /*!
559  * \brief Match command.
560  *
561  * \param argc Command argument count.
562  * \param argv Array of arguments.
563  * \param cmdMap Command map to search.
564  *
565  * \return Returns pointer to matched command on success. NULL otherwise.
566  */
567 DynaShellCmd *DynaShell::MatchCmd(int argc, char *argv[], ShCmdMap &cmdMap)
568 {
569  ShCmdMap::iterator iter1, iter2;
570  char *sPattern;
571  const char *sCmdName1, *sCmdName2;
572  size_t n;
573  bool_t bIsUnique = true;
574  int cnt;
575 
576  if( argc == 0 )
577  {
578  Error("No Command.");
579  return NULL;
580  }
581 
582  sPattern = argv[0];
583 
584  n = strlen(sPattern);
585 
586  // find command
587  for(iter1=cmdMap.begin(); iter1!=cmdMap.end(); iter1++)
588  {
589  sCmdName1 = iter1->first;
590  if( !strncmp(sPattern, sCmdName1, n) )
591  {
592  break;
593  }
594  }
595 
596  // no matching command found
597  if( iter1 == cmdMap.end() )
598  {
599  Error("%s: Command not found.\n", sPattern);
600  return NULL;
601  }
602 
603  // verify uniqueness
604  for(iter2=cmdMap.begin(); iter2!=cmdMap.end(); iter2++)
605  {
606  if( iter2 == iter1 )
607  {
608  continue;
609  }
610 
611  sCmdName2 = iter2->first;
612 
613  // another match
614  if( !strncmp(sPattern, sCmdName2, n) )
615  {
616  cnt++;
617 
618  // first non-unique
619  if( bIsUnique )
620  {
621  fprintf(stderr, "%s: Command not unique: Matches %s %s",
622  sPattern, sCmdName1, sCmdName2);
623  bIsUnique = false;
624  }
625 
626  // subsequent non-uniques
627  else if( cnt < 8 )
628  {
629  fprintf(stderr, " %s", sCmdName2);
630  }
631 
632  // too many to print
633  else
634  {
635  fprintf(stderr, " ...");
636  break;
637  }
638  }
639  }
640 
641  if( !bIsUnique )
642  {
643  fprintf(stderr, "\n");
644  return NULL;
645  }
646 
647  if( iter1->second.IsCmd() )
648  {
649  return iter1->second.GetCmd();
650  }
651  else if( iter1->second.IsMap() )
652  {
653  return MatchCmd(argc-1, argv+1, *iter1->second.GetMap());
654  }
655  else
656  {
657  return NULL;
658  }
659 }
660 
661 /*!
662  * \brief Push new script on the stack.
663  *
664  * The new script becomes the point of execution.
665  *
666  * \param sScriptFile Script file path name.
667  * \param fp Script file opened file pointer.
668  */
669 void DynaShell::ScriptPush(const char *sScriptFile, FILE *fp)
670 {
671  ShScript *pScript;
672 
673  if( (int)m_vecScripts.size() >= m_nMaxScriptDepth )
674  {
675  Error("Cannot execute script %s: At maximum script depth of %d",
676  sScriptFile, m_nMaxScriptDepth);
677  }
678  else
679  {
680  pScript = new ShScript(sScriptFile, fp);
681  m_vecScripts.push_back(pScript);
682  }
683 }
684 
685 /*!
686  * \brief Pop the current script from stack.
687  *
688  * The new top-of-stack script resumes execution. If there are no more scripts
689  * on the stack and this shell is not user interactive, then the shell will
690  * marked for termination. Otherwise, if the stack is empty, standard input
691  * becomes the point of execution.
692  */
694 {
695  ShScript *pScript;
696 
697  if( (int)m_vecScripts.size() > 0 )
698  {
699  pScript = m_vecScripts.back();
700  m_vecScripts.pop_back();
701  delete pScript;
702  }
703 
704  if( !m_bIsInteractive && m_vecScripts.empty() )
705  {
706  m_bRun = false;
707  }
708 }
709 
710 /*!
711  * \brief Flush all scripts from stack.
712  *
713  * If this shell is not user interactive, the shell will marked for termination.
714  * Otherwise, standard input becomes the point of execution.
715  */
717 {
718  ShScript *pScript;
719  int i;
720 
721  for(i=0; i<(int)m_vecScripts.size(); ++i)
722  {
723  pScript = m_vecScripts.back();
724  m_vecScripts.pop_back();
725  delete pScript;
726  }
727 
728  if( !m_bIsInteractive )
729  {
730  m_bRun = false;
731  }
732 }
733 
734 /*!
735  * \brief Trace script execution.
736  *
737  * \param sLine Executable script statement.
738  */
739 void DynaShell::ScriptTrace(const char *sLine)
740 {
741  if( IsRunningScript() )
742  {
743  if( m_bIsInteractive )
744  {
745  printf(": %s\n", sLine);
746  }
747  else
748  {
749  printf("%s[%d]: %s\n",
750  m_vecScripts.back()->GetScriptName(),
751  m_vecScripts.back()->GetLineNum(),
752  sLine);
753  }
754  }
755 }
756 
757 /*!
758  * \brief Print formatted success response.
759  *
760  * \param sFmt Format string.
761  * \param ... Variable argument list.
762  */
763 void DynaShell::Response(const char *sFmt, ...)
764 {
765  va_list ap;
766 
767  if( !m_bSilent )
768  {
769  va_start(ap, sFmt);
770  vprintf(sFmt, ap);
771  va_end(ap);
772  }
773 }
774 
775 /*!
776  * \brief Issue warning.
777  *
778  * \param sFmt Format string.
779  * \param ... Optional format string arguments.
780  */
781 void DynaShell::Warning(const char *sFmt, ...)
782 {
783  va_list ap;
784 
785  if( !m_vecScripts.empty() )
786  {
787  fprintf(stderr, "%s[%d]: ",
788  m_vecScripts.back()->GetScriptName(),
789  m_vecScripts.back()->GetLineNum());
790  }
791 
792  fprintf(stderr, "Warning: ");
793 
794  va_start(ap, sFmt);
795  vfprintf(stderr, sFmt, ap);
796  va_end(ap);
797 
798  fprintf(stderr, "\n");
799 }
800 
801 /*!
802  * \brief Raise error on dynamixel error code.
803  *
804  * \param rc \ref dyna_ecodes.
805  * \param sFmt Format string.
806  * \param ... Optional format string arguments.
807  */
808 void DynaShell::Error(int rc, const char *sFmt, ...)
809 {
810  char buf[256];
811  va_list ap;
812 
813  va_start(ap, sFmt);
814 
815  vsnprintf(buf, sizeof(buf), sFmt, ap);
816  buf[sizeof(buf)-1] = 0;
817 
818  va_end(ap);
819 
820  Error("%s: %s", DynaStrError(rc), buf);
821 }
822 
823 /*!
824  * \brief Raise error on alarmed servo.
825  *
826  * \param nServoId Servo id.
827  * \param uAlarms \ref dyna_memmap_gen_alarm_shutdown.
828  */
829 void DynaShell::Error(int nServoId, uint_t uAlarms)
830 {
831  string strAlarms;
832 
833  strAlarms = DynaComm::GetAlarmsString(uAlarms);
834 
835  Error("Servo %d: Alarms: %s.", nServoId, strAlarms.c_str());
836 }
837 
838 /*!
839  * \brief Raise error.
840  *
841  * If the error occurred while running a script, the script is aborted.
842  * If the shell is non-interactive, the shell is terminated.
843  *
844  * \param sFmt Format string.
845  * \param ... Optional format string arguments.
846  */
847 void DynaShell::Error(const char *sFmt, ...)
848 {
849  va_list ap;
850 
851  if( !m_vecScripts.empty() )
852  {
853  fprintf(stderr, "%s[%d]: ",
854  m_vecScripts.back()->GetScriptName(),
855  m_vecScripts.back()->GetLineNum());
856  }
857 
858  fprintf(stderr, "Error: ");
859 
860  va_start(ap, sFmt);
861  vfprintf(stderr, sFmt, ap);
862  va_end(ap);
863 
864  fprintf(stderr, "\n");
865 
867 
868  ScriptFlush();
869 
870  if( !m_bIsInteractive )
871  {
872  m_bRun = false;
873  }
874 }
875 
876 /*!
877  * \brief Command execution loop.
878  *
879  * Loop through all commands from input intil EOF, error, or user commanded
880  * termination.
881  *
882  * \return DYNA_OK on successful execution, \h_lt 0 on failure.
883  */
885 {
886  int tokc;
887  char *tokv[1024];
888  DynaShellCmd *pCmd;
889  int nOffset; // command-line arguments start offset
890  int rc;
891 
892  if( m_bIsInteractive )
893  {
894  printf(" Dynamixel Simple Shell\n");
895  printf("(enter 'help' for list of commands; "
896  "partial command matching supported)\n\n");
897  }
898 
899  while( m_bRun )
900  {
901  // clear any old shell command return code
902  m_rc = DYNA_OK;
903 
904  // parse [compound] statement
905  rc = GetInput();
906 
907  //
908  // test terminating conditions
909  //
910  switch( rc )
911  {
912  // parse and execute
913  case SH_RC_OK:
914  // tokenize input
915  tokc = ReadLine::tokenize(m_sInputLine, tokv, arraysize(tokv));
916 
917  if( tokc <= 0 )
918  {
919  continue;
920  }
921 
922  // find command
923  pCmd = MatchCmd(tokc, tokv, m_mapCmds);
924 
925  if( pCmd != NULL )
926  {
927  nOffset = pCmd->GetPublishedLevel() + 1;
928 
929  // execute command
930  pCmd->Exec(*this, tokc-nOffset, tokv+nOffset);
931  }
932  break;
933 
934  // parse but don't execute
935  case SH_RC_OK_NOEXEC:
936  break;
937 
938  // end-of-file
939  case SH_RC_EOF:
940  ScriptPop();
941  break;
942 
943  // system or other error
944  case SH_RC_SYS_ERROR:
945  default:
946  // if not interactive terminate loop
947  if( !m_bIsInteractive )
948  {
949  m_rc = -DYNA_ECODE_SYS;
950  m_bRun = false;
951  }
952  break;
953  }
954  }
955 
956  return m_rc;
957 }
RoadNarrows Dynamixel Bus Communications Abstract Base Class Interface.
int RegisterGenerator(const string strRegEx, ReadLineAppGenFunc_T fnAppGen, void *pAppArg)
Register application-specific tab-completion generator associated.
char * GetInputLine()
Get one input line from file stream.
Definition: dynashell.cxx:469
void ScriptBumpLineNum()
Bump the line number of the current executing script, if any, by one.
Definition: dynashell.h:527
FILE * InputFp()
Get the current input file pointer.
Definition: dynashell.h:518
#define DYNA_OK
not an error, success
Definition: Dynamixel.h:78
bool IsRunningScript()
Test if executing a script.
Definition: dynashell.h:416
Dynamixel shell utilities.
void ScriptTrace(const char *sLine)
Trace script execution.
Definition: dynashell.cxx:739
DynaComm * m_pDynaComm
dynamixel bus communication
Definition: dynashell.h:359
void ScriptPush(const char *sScriptFile, FILE *fp)
Push new script on the stack.
Definition: dynashell.cxx:669
static char * CmdSpecGeneratorWrap(int nUid, const char *sText, size_t uTextLen, int nState, const char *sContext, void *pArg)
Command spec generator wrapper function.
Definition: dynashell.cxx:391
DynaShellCmd * MatchCmd(int argc, char *argv[], ShCmdMap &cmdMap)
Match command.
Definition: dynashell.cxx:567
#define SH_RC_OK_NOEXEC
shell input ok, but don&#39;t execute command
Definition: dynashell.cxx:82
char * CmdListGenerator(int nUid, const char *sText, size_t uTextLen, int nState, const char *sContext)
Command list generator function.
Definition: dynashell.cxx:319
ShCmdMap m_mapCmds
map of published commnds
Definition: dynashell.h:472
static char * fReadLine(FILE *fp, const char *sPrompt)
Read one input line from the given input stream.
MapTabCompleteGen m_mapTabGen
command tab-completion generators map
Definition: dynashell.h:473
void PublishMap(const char *sParent, const string &strBrief)
Publish new command map to shell.
Definition: dynashell.cxx:159
static const int FIRST
first state
bool IsExecStmt(const char *sLine)
Test if line is a potential executable statement.
Definition: dynashell.h:492
static char * dupstr(const string &str)
Duplicate string.
Dynamixel shell command abstract base class.
Definition: dynashell_cmd.h:80
#define DYNA_ECODE_SYS
system (errno) error
Definition: Dynamixel.h:81
void InputInit()
Initialize input.
Definition: dynashell.cxx:271
static int tokenize(char *s, char *tokv[], size_t tokmax)
Tokenize input.
static char * CmdListGeneratorWrap(int nUid, const char *sText, size_t uTextLen, int nState, const char *sContext, void *pArg)
Command list generator wrapper function.
Definition: dynashell.cxx:292
DynaRecording * m_pRecording
dynamixel recording
Definition: dynashell.h:362
vector< ShScript * > m_vecScripts
script stack
Definition: dynashell.h:474
const char * DynaStrError(int ecode)
Get the error string describing the Dynamixel error code.
Definition: DynaError.cxx:141
virtual int Stop()
Stop control and monitoring tasks.
#define SH_RC_SYS_ERROR
system error on shell input
Definition: dynashell.cxx:84
Shell Script Class.
Definition: dynashell.h:270
int SetPublishedInfo(int nLevel, const char *sParent=NULL)
map< const char *, ShCmdNode, ShCmdNodeKeyCmp > ShCmdMap
Shell command map type.
Definition: dynashell.h:107
bool m_bIsInteractive
[not] user interactive
Definition: dynashell.h:356
void AddToHistory(char *sLine)
Add command line to history.
Definition: dynashell.cxx:443
void ScriptPop()
Pop the current script from stack.
Definition: dynashell.cxx:693
void Response(const char *sFmt,...)
Print formatted success response.
Definition: dynashell.cxx:763
#define DYNA_ECODE_RUNTIME
Shell run-time error.
Definition: Dynamixel.h:102
void ScriptFlush()
Flush all scripts from stack.
Definition: dynashell.cxx:716
bool m_bXTrace
do [not] trace script
Definition: dynashell.h:357
const char * m_sPS1
primary user prompt
Definition: dynashell.h:467
void Warning(const char *sFmt,...)
Issue warning.
Definition: dynashell.cxx:781
ReadLine m_ReadLine
readline object
Definition: dynashell.h:471
void AddToHistory(const char *sInput)
Add line to history.
RoadNarrows Dynamixel Bus Communication over Serial Interface Class Interface.
bool IsEOF()
Test if input is at End Of File condition.
Definition: dynashell.h:540
int GetPublishedLevel() const
const char * GetCmdName()
Get dynamixel shell command name.
char * CmdSpecGenerator(int nUid, const char *sText, size_t uTextLen, int nState, const char *sContext)
Command spec generator function.
Definition: dynashell.cxx:418
static std::string GetAlarmsString(const uint_t uAlarms, const std::string &strSep="; ")
Get a formatted servo alarms string associated with the alarms.
Definition: DynaComm.cxx:312
DynaBgThread * m_pDynaBgThread
dynamixel chain
Definition: dynashell.h:361
RoadNarrows Dynamixel Archetype Servo Abstract Base Class.
static const int NOT_REG
not registered return value
bool m_bSilent
do [not] silence non-error responses
Definition: dynashell.h:358
The Dynamixel Shell ReadLine Class.
void PublishCommand(const char *sParent, DynaShellCmd *pNewCmd)
Publish new command to shell.
Definition: dynashell.cxx:192
RoadNarrows Dynamixel Top-Level Package Header File.
The dynashell Command Class Interface.
const char * GetCmdHelpBrief()
Get shell command name brief description.
void DeleteCommands(ShCmdMap &cmdMap)
Delete all published commands in map.
Definition: dynashell.cxx:249
char * iReadLine(const char *sPrompt)
Interactively read a line of input from standard input.
RoadNarrows Botsene IP Proxied Dynamixel Bus Communication Class Interface.
#define SH_RC_EOF
end of file
Definition: dynashell.cxx:83
bool m_bRun
do [not] run shell
Definition: dynashell.h:355
#define SH_RC_OK
shell input ok
Definition: dynashell.cxx:81
const char * GetPublishedName() const
DynaChain * m_pDynaChain
dynamixel chain
Definition: dynashell.h:360
void Error(int rc, const char *sFmt,...)
Raise error on dynamixel error code.
Definition: dynashell.cxx:808
int Run()
Command execution loop.
Definition: dynashell.cxx:884
int m_rc
last command executed return code
Definition: dynashell.h:475
int GetInput()
Get one, non-empty input line.
Definition: dynashell.cxx:505
static const int m_nMaxScriptDepth
maximum script stack depth
Definition: dynashell.h:353
The simple dynashell declarations.
bool m_bNoExec
parse only, no execution
Definition: dynashell.h:469
char * m_sInputLine
working, allocated line of input
Definition: dynashell.h:470
virtual void Exec(DynaShell &shell, int argc, char *argv[])=0
Execute command abstract function.