Dynamixel  2.9.5
RoadNarrows Robotics Dynamixel Package
dynashell_cmd.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Dynamixel
4 //
5 // Program: dynashell
6 //
7 // File: dynashell_cmd.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 Command Base and Derived Core Classes.
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 <sys/time.h>
51 #include <sys/types.h>
52 #include <stdio.h>
53 #include <unistd.h>
54 #include <ctype.h>
55 
56 #include <cstring>
57 #include <iostream>
58 #include <fstream>
59 
60 #include "rnr/rnrconfig.h"
61 #include "rnr/log.h"
62 
63 #include "Dynamixel/Dynamixel.h"
64 #include "Dynamixel/DynaComm.h"
65 #include "Dynamixel/DynaServo.h"
66 #include "Dynamixel/DynaChain.h"
67 #include "Dynamixel/DynaBgThread.h"
68 
69 #include "dynashell.h"
70 #include "dynashell_cmd.h"
71 #include "dynashell_util.h"
72 
73 using namespace std;
74 
75 
76 // -----------------------------------------------------------------------------
77 // DynaShellCmd Class
78 // -----------------------------------------------------------------------------
79 
80 /*!
81  * \brief Print command help with the description aligned at the given
82  * indentation.
83  *
84  * \param indent Column indentation.
85  * \param width Column width.
86  */
87 void DynaShellCmd::PrintHelp(int indent, int width)
88 {
89  int col; // current column position
90 
91  // print brief
92  PrintDelim(width, '-');
93  col = printf("%s - ", m_sPubName);
94  PrintBlock(col, indent, width, m_sCmdHelpBrief);
95 
96  // print synopses
97  if( (m_sCmdHelpArgs != NULL) && (*m_sCmdHelpArgs != 0) )
98  {
99  printf("\n");
100  PrintSynopses(indent, width);
101  }
102 
103 
104  // print description
105  if( (m_sCmdHelpDesc != NULL) && (*m_sCmdHelpDesc != 0) )
106  {
107  printf("\n");
108  PrintBlock(0, indent, width, m_sCmdHelpDesc);
109  }
110 }
111 
112 /*!
113  * \brief Print synsopses.
114  *
115  * \param indent Column indentation.
116  * \param width Column width.
117  */
118 void DynaShellCmd::PrintSynopses(int indent, int width)
119 {
120  int col; // current column position
121  char buf[256]; // working buffer
122  const char *s, *t; // working pointers
123  size_t len; // working length
124  bool bFirst; // first synopsis
125  bool bLast ; // last synopsis
126 
127  if( (m_sCmdHelpArgs != NULL) && (*m_sCmdHelpArgs != 0) )
128  {
129  for(s=m_sCmdHelpArgs, bFirst=true, bLast=false; !bLast; )
130  {
131  t = strchr(s, '\n');
132 
133  if( t != NULL )
134  {
135  len = (size_t)(t-s);
136  }
137  else
138  {
139  len = strlen(s);
140  bLast = true;
141  }
142  if( len >= sizeof(buf) )
143  {
144  len = sizeof(buf)-1;
145  }
146 
147  strncpy(buf, s, len);
148  buf[len] = 0;
149 
150  if( bFirst )
151  {
152  col = printf("%*sUsage: %s ", indent, "", m_sPubName);
153  bFirst = false;
154  }
155  else
156  {
157  col = printf("%*s %s ", indent, "", m_sPubName);
158  }
159 
160  PrintBlock(col, col, width, buf);
161 
162  if( t != NULL )
163  {
164  s = t + 1;
165  }
166  }
167  }
168 }
169 
170 /*!
171  * \brief Print a block of indented text of width.
172  *
173  * \param col Current column position.
174  * \param indent Column indentation from 0.
175  * \param width Column width from 0;
176  * \param sText Text to print.
177  */
178 void DynaShellCmd::PrintBlock(int col, int indent, int width, const char *sText)
179 {
180  int n; // word length
181  const char *s, *t; // working pointers
182 
183  if( col >= width )
184  {
185  printf("\n");
186  col = 0;
187  }
188 
189  for(s=sText; *s != 0; )
190  {
191  // find end of word
192  t = eow(s);
193 
194  // end of string
195  if( t == NULL )
196  {
197  n = (int)strlen(s);
198  t = s + n;
199  }
200 
201  // got a word
202  else
203  {
204  n = (int)(t - s);
205  }
206 
207  // will exceed line limit, go to next line
208  if( col+n >= width )
209  {
210  printf("\n");
211  col = printf("%*s", indent, "");
212  }
213 
214  // print help word
215  if( n > 0 )
216  {
217  col += printf("%.*s", n, s);
218  }
219  //col += printf("|");
220 
221  switch(*t)
222  {
223  case ' ':
224  case '\t':
225  case '\v':
226  col += printf(" ");
227  s = ++t;
228  break;
229  case '\n':
230  case '\r':
231  printf("\n");
232  col = printf("%*s", indent, "");
233  s = ++t;
234  break;
235  case 0:
236  default:
237  s = t;
238  }
239  }
240 
241  printf("\n");
242 }
243 
244 void DynaShellCmd::PrintDelim(int width, const char cDelim)
245 {
246  while( width-- > 0 )
247  {
248  printf("%c", cDelim);
249  }
250  printf("\n");
251 }
252 
253 /*!
254  * \brief Find end of word.
255  *
256  * A word is define as a set of non-whitespace characters delimitedd by white
257  * space.
258  *
259  * \param s Null-terminated string to search.
260  *
261  * \return Returns a pointer to the first trailing white space character or
262  * NULL if no white space found.
263  */
264 char *DynaShellCmd::eow(const char *s)
265 {
266  char *t;
267 
268  if( (s == NULL) || (*s == 0) )
269  {
270  return NULL;
271  }
272 
273  for(t=(char *)s; *t; ++t)
274  {
275  if( isspace(*t) )
276  {
277  break;
278  }
279  }
280 
281  return *t? t: NULL;
282 }
283 
284 
285 // -----------------------------------------------------------------------------
286 // DynaShellCmdChainIn Class
287 // -----------------------------------------------------------------------------
288 
289 /*!
290  * \brief Execute 'read-like' command on servos.
291  *
292  * \param shell Dynamixel shell.
293  * \param argc Command argument count.
294  * \param argv Array of arguments.
295  */
296 void DynaShellCmdChainIn::Exec(DynaShell &shell, int argc, char *argv[])
297 {
298  int nServoId;
299  int iter;
300  DynaServo *pServo;
301 
302  TRY( ChkArgCnt(shell, argc) );
303 
304  TRY( ChkComm(shell) );
305  TRY( ChkChainNotEmpty(shell) );
306 
307  if( !strcmp(argv[0], "chain") )
308  {
309  TRY( ChkArgCntEQ(shell, argc, 1) );
310 
311  if( m_bMasterOpOnly )
312  {
313  for(nServoId = shell.m_pDynaChain->IterStartMaster(&iter);
314  nServoId != DYNA_ID_NONE;
315  nServoId = shell.m_pDynaChain->IterNextMaster(&iter))
316  {
317  pServo = shell.m_pDynaChain->GetServo(nServoId);
318  doExec(shell, pServo);
319  }
320  }
321  else
322  {
323  for(nServoId = shell.m_pDynaChain->IterStart(&iter);
324  nServoId != DYNA_ID_NONE;
325  nServoId = shell.m_pDynaChain->IterNext(&iter))
326  {
327  pServo = shell.m_pDynaChain->GetServo(nServoId);
328  doExec(shell, pServo);
329  }
330  }
331  }
332 
333  else
334  {
335  for(iter=0; iter<argc; ++iter)
336  {
337  TRY( ToInt(shell, argv[iter], &nServoId) );
338  TRY( ChkChainHasServo(shell, nServoId) );
339  TRY( !m_bMasterOpOnly || ChkChainIsMasterServo(shell, nServoId) );
340 
341  pServo = shell.m_pDynaChain->GetServo(nServoId);
342  doExec(shell, pServo);
343  }
344  }
345 }
346 
347 /*!
348  * \brief Command tab completion generator.
349  *
350  * Completes <servo_id> | chain [servo_id [servo_id ...]]
351  *
352  * \param shell Dynamixel shell.
353  * \param sText Partial text string to complete.
354  * \param uTextLen Length of text.
355  * \param nState Generator state. If FIRST, then initialize any statics.
356  * \param sContext Generator context (i.e. canonical command path).
357  *
358  * \return
359  * If a first/next match is made, return allocated completed match.\n
360  * Otherwise return NULL.
361  */
363  const char *sText,
364  size_t uTextLen,
365  int nState,
366  const char *sContext)
367 {
368  char buf[16];
369 
370  if( (shell.m_pDynaChain == NULL) ||
371  (shell.m_pDynaChain->GetNumberInChain() == 0) )
372  {
373  return NULL;
374  }
375 
376  if( m_bMasterOpOnly && shell.m_pDynaChain->GetNumberOfMastersInChain() == 0 )
377  {
378  return NULL;
379  }
380 
381  //
382  // New command to complete - initialize.
383  //
384  if( nState == ReadLine::FIRST )
385  {
386  if( m_bMasterOpOnly )
387  {
388  m_nTabServoId = shell.m_pDynaChain->IterStartMaster(&m_nTabIter);
389  }
390  else
391  {
392  m_nTabServoId = shell.m_pDynaChain->IterStart(&m_nTabIter);
393  }
394 
395  if( ReadLine::wc(sContext) == ReadLine::wc(m_sPubName) )
396  {
397  m_bTabIncChain = true;
398  }
399  else
400  {
401  m_bTabIncChain = false;
402  }
403  }
404 
405  while( m_nTabServoId != DYNA_ID_NONE )
406  {
407  snprintf(buf, sizeof(buf), "%d", m_nTabServoId);
408  buf[sizeof(buf)-1] = 0;
409 
410  if( m_bMasterOpOnly )
411  {
412  m_nTabServoId = shell.m_pDynaChain->IterNextMaster(&m_nTabIter);
413  }
414  else
415  {
416  m_nTabServoId = shell.m_pDynaChain->IterNext(&m_nTabIter);
417  }
418 
419  if( !strncmp(buf, sText, uTextLen) )
420  {
421  return ReadLine::dupstr(buf);
422  }
423  }
424 
425  if( m_bTabIncChain )
426  {
427  m_bTabIncChain = false;
428  if( !strncmp("chain", sText, uTextLen) )
429  {
430  return ReadLine::dupstr("chain");
431  }
432  }
433 
434  // no more matches
435  return NULL;
436 }
437 
438 
439 // -----------------------------------------------------------------------------
440 // DynaShellCmdChainOut Class
441 // -----------------------------------------------------------------------------
442 
443 /*!
444  * \brief Execute 'write-like' command on servos.
445  *
446  * \param shell Dynamixel shell.
447  * \param argc Command argument count.
448  * \param argv Array of arguments.
449  */
450 void DynaShellCmdChainOut::Exec(DynaShell &shell, int argc, char *argv[])
451 {
452  uint_t uNumPairs;
454  int nServoId;
455  int nVal;
456  int iter;
457  int cnt;
458 
459  TRY( ChkArgCnt(shell, argc) );
460 
461  if( (argc % 2) != 0 )
462  {
463  shell.Error("Unmatched servo_id,value pairs.");
464  return;
465  }
466 
467  uNumPairs = (uint_t)(argc / 2);
468 
469  if( uNumPairs > DYNA_ID_NUMOF )
470  {
471  shell.Error("%u: Too many servo_id,value pairs.", uNumPairs);
472  return;
473  }
474 
475  TRY( ChkComm(shell) );
476  TRY( ChkChainNotEmpty(shell) );
477 
478  // full chain of servos
479  if( !strcmp(argv[0], "chain") )
480  {
481  TRY( ChkArgCntEQ(shell, argc, 2) );
482  TRY( ArgToInt(shell, argv[1], &nVal) );
483 
484  if( m_bMasterOpOnly )
485  {
486  for(nServoId = shell.m_pDynaChain->IterStartMaster(&iter), cnt=0;
487  nServoId != DYNA_ID_NONE;
488  nServoId = shell.m_pDynaChain->IterNextMaster(&iter), ++cnt)
489  {
490  tup[cnt].m_nServoId = nServoId;
491  tup[cnt].m_nVal = nVal;
492  }
493  }
494  else
495  {
496  for(nServoId = shell.m_pDynaChain->IterStart(&iter), cnt=0;
497  nServoId != DYNA_ID_NONE;
498  nServoId = shell.m_pDynaChain->IterNext(&iter), ++cnt)
499  {
500  tup[cnt].m_nServoId = nServoId;
501  tup[cnt].m_nVal = nVal;
502  }
503  }
504 
505  uNumPairs = (uint_t)cnt;
506  }
507 
508  // list of servos
509  else
510  {
511  for(iter=0, cnt=0; iter<argc; iter+=2, ++cnt)
512  {
513  TRY( ToInt(shell, argv[iter], &nServoId) );
514  TRY( ArgToInt(shell, argv[iter+1], &nVal) );
515  TRY( ChkChainHasServo(shell, nServoId) );
516  TRY( !m_bMasterOpOnly || ChkChainIsMasterServo(shell, nServoId) );
517 
518  tup[cnt].m_nServoId = nServoId;
519  tup[cnt].m_nVal = nVal;
520  }
521  }
522 
523  doExec(shell, tup, uNumPairs);
524 }
525 
526 /*!
527  * \brief Command tab completion generator.
528  *
529  * Completes <servo_id> | chain NULL [servo_id NULL [servo_id NULL ...]]
530  *
531  * \param shell Dynamixel shell.
532  * \param sText Partial text string to complete.
533  * \param uTextLen Length of text.
534  * \param nState Generator state. If FIRST, then initialize any statics.
535  * \param sContext Generator context (i.e. canonical command path).
536  *
537  * \return
538  * If a first/next match is made, return allocated completed match.\n
539  * Otherwise return NULL.
540  */
542  const char *sText,
543  size_t uTextLen,
544  int nState,
545  const char *sContext)
546 {
547  char buf[16];
548  int nArgNum;
549 
550  if( (shell.m_pDynaChain == NULL) ||
551  (shell.m_pDynaChain->GetNumberInChain() == 0) )
552  {
553  return NULL;
554  }
555 
556  else if( m_bMasterOpOnly &&
557  (shell.m_pDynaChain->GetNumberOfMastersInChain() == 0) )
558  {
559  return NULL;
560  }
561 
562  // argument number of already (expanded) arguments
563  nArgNum = ReadLine::wc(sContext) - ReadLine::wc(m_sPubName);
564 
565  // every odd argument is the servo value, which cannot be expanded
566  if( nArgNum & 0x01 )
567  {
568  return NULL;
569  }
570 
571  //
572  // New command to complete - initialize.
573  //
574  if( nState == ReadLine::FIRST )
575  {
576  if( m_bMasterOpOnly )
577  {
578  m_nTabServoId = shell.m_pDynaChain->IterStartMaster(&m_nTabIter);
579  }
580  else
581  {
582  m_nTabServoId = shell.m_pDynaChain->IterStart(&m_nTabIter);
583  }
584 
585  if( nArgNum == 0 )
586  {
587  m_bTabIncChain = true;
588  }
589  else
590  {
591  m_bTabIncChain = false;
592  }
593  }
594 
595  while( m_nTabServoId != DYNA_ID_NONE )
596  {
597  snprintf(buf, sizeof(buf), "%d", m_nTabServoId);
598  buf[sizeof(buf)-1] = 0;
599 
600  if( m_bMasterOpOnly )
601  {
602  m_nTabServoId = shell.m_pDynaChain->IterNextMaster(&m_nTabIter);
603  }
604  else
605  {
606  m_nTabServoId = shell.m_pDynaChain->IterNext(&m_nTabIter);
607  }
608 
609  if( !strncmp(buf, sText, uTextLen) )
610  {
611  return ReadLine::dupstr(buf);
612  }
613  }
614 
615  if( m_bTabIncChain )
616  {
617  m_bTabIncChain = false;
618  if( !strncmp("chain", sText, uTextLen) )
619  {
620  return ReadLine::dupstr("chain");
621  }
622  }
623 
624  // no more matches
625  return NULL;
626 }
RoadNarrows Dynamixel Bus Communications Abstract Base Class Interface.
int m_nServoId
servo id
char * eow(const char *s)
Find end of word.
Dynamixel shell utilities.
Definition: t.py:1
virtual void PrintBlock(int col, int indent, int width, const char *sText)
Print a block of indented text of width.
virtual void Exec(DynaShell &shell, int argc, char *argv[])
Execute &#39;read-like&#39; command on servos.
virtual uint_t GetNumberOfMastersInChain()
Get the number of servos currently in chain.
Definition: DynaChain.cxx:182
RoadNarrows Dynamixel Servo Chain Container Base Class Interface.
virtual void Exec(DynaShell &shell, int argc, char *argv[])
Execute &#39;write-like&#39; command on servos.
static const int FIRST
first state
static char * dupstr(const string &str)
Duplicate string.
virtual int IterStartMaster(int *pIter)
Start iteration master servos over of entire servo chain.
Definition: DynaChain.cxx:1036
#define DYNA_ID_NUMOF
number of unique servo id&#39;s
Definition: Dynamixel.h:148
virtual int IterNextMaster(int *pIter)
Next iteration of master servos over of entire servo chain.
Definition: DynaChain.cxx:1058
virtual uint_t GetNumberInChain() const
Get the number of servos currently in chain.
Definition: DynaChain.h:140
Dynamixel Servo Abstract Base Class.
Definition: DynaServo.h:78
Dynamixel background thread class declarations.
Execute 2-tuple structure type.
static int wc(const string &str)
Count the words in the string.
virtual void PrintHelp(int indent=0, int width=80)
Print command help with the description aligned at the given indentation.
virtual int IterStart(int *pIter)
Start iteration over of entire servo chain.
Definition: DynaChain.cxx:1008
virtual DynaServo * GetServo(int nServoId)
Definition: DynaChain.h:129
int m_nVal
value
RoadNarrows Dynamixel Archetype Servo Abstract Base Class.
virtual char * TabCompletion(DynaShell &shell, const char *sText, size_t uTextLen, int nState, const char *sContext)
Command tab completion generator.
#define TRY(boolexpr,...)
Try boolean expression.
Definition: dynashell_cmd.h:89
RoadNarrows Dynamixel Top-Level Package Header File.
The dynashell Command Class Interface.
#define DYNA_ID_NONE
no servo id
Definition: Dynamixel.h:145
virtual int IterNext(int *pIter)
Next iteration over of entire servo chain.
Definition: DynaChain.cxx:1020
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
virtual void PrintSynopses(int indent, int width)
Print synsopses.
The simple dynashell declarations.
virtual char * TabCompletion(DynaShell &shell, const char *sText, size_t uTextLen, int nState, const char *sContext)
Command tab completion generator.