OrocosComponentLibrary  2.9.0
TaskBrowser.cpp
1 #ifndef NO_GPL
2 /***************************************************************************
3  tag: Peter Soetens Thu Jul 3 15:31:34 CEST 2008 TaskBrowser.cpp
4 
5  TaskBrowser.cpp - description
6  -------------------
7  begin : Thu July 03 2008
8  copyright : (C) 2008 Peter Soetens
9  email : peter.soetens@fmtc.be
10 
11  ***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
21  * General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public *
24  * License along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 59 Temple Place, *
26  * Suite 330, Boston, MA 02111-1307 USA *
27  ***************************************************************************/
28 #else
29 /***************************************************************************
30  tag: Peter Soetens Tue Dec 21 22:43:07 CET 2004 TaskBrowser.cxx
31 
32  TaskBrowser.cxx - description
33  -------------------
34  begin : Tue December 21 2004
35  copyright : (C) 2004 Peter Soetens
36  email : peter.soetens@mech.kuleuven.ac.be
37 
38  ***************************************************************************
39  * This library is free software; you can redistribute it and/or *
40  * modify it under the terms of the GNU Lesser General Public *
41  * License as published by the Free Software Foundation; either *
42  * version 2.1 of the License, or (at your option) any later version. *
43  * *
44  * This library is distributed in the hope that it will be useful, *
45  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
47  * Lesser General Public License for more details. *
48  * *
49  * You should have received a copy of the GNU Lesser General Public *
50  * License along with this library; if not, write to the Free Software *
51  * Foundation, Inc., 59 Temple Place, *
52  * Suite 330, Boston, MA 02111-1307 USA *
53  * *
54  ***************************************************************************/
55 #endif
56 
57 
58 #include <rtt/Logger.hpp>
59 #include <rtt/extras/MultiVector.hpp>
60 #include <rtt/types/TypeStream.hpp>
61 #include <rtt/types/Types.hpp>
62 #include "TaskBrowser.hpp"
63 
64 #include <rtt/scripting/TryCommand.hpp>
65 #include <rtt/TaskContext.hpp>
66 #include <rtt/plugin/PluginLoader.hpp>
67 #include <rtt/scripting/parser-debug.hpp>
68 #include <rtt/scripting/Parser.hpp>
69 #include <rtt/scripting/parse_exception.hpp>
70 #include <rtt/scripting/PeerParser.hpp>
71 #include <rtt/scripting/Scripting.hpp>
72 #include <rtt/plugin/PluginLoader.hpp>
73 #include <rtt/internal/GlobalService.hpp>
74 #include <rtt/types/GlobalsRepository.hpp>
75 #include <rtt/internal/GlobalEngine.hpp>
76 #include <boost/algorithm/string.hpp>
77 
78 #include <iostream>
79 #include <fstream>
80 #include <sstream>
81 #include <iomanip>
82 #include <deque>
83 #include <stdio.h>
84 #include <algorithm>
85 
86 #if defined(HAS_READLINE) && !defined(NO_GPL)
87 # define USE_READLINE
88 #endif
89 #if defined(HAS_EDITLINE)
90 // we use the readline bc wrapper:
91 # define USE_READLINE
92 # define USE_EDITLINE
93 #endif
94 // only use signals if posix, and pure readline
95 # if defined(_POSIX_VERSION) && defined(HAS_READLINE) && !defined(HAS_EDITLINE)
96 # define USE_SIGNALS 1
97 # endif
98 
99 
100 #ifdef USE_READLINE
101 # ifdef USE_EDITLINE
102 # include <editline/readline.h>
103 # else
104 # include <readline/readline.h>
105 # include <readline/history.h>
106 # endif
107 #endif
108 #include <boost/bind.hpp>
109 #include <boost/lambda/lambda.hpp>
110 
111 #ifdef USE_SIGNALS
112 #include <signal.h>
113 #endif
114 
115 // we need to declare it since Xenomai does not declare it in any header
116 #if defined(USE_SIGNALS) && defined(OROCOS_TARGET_XENOMAI) && CONFIG_XENO_VERSION_MAJOR == 2 && CONFIG_XENO_VERSION_MINOR >= 5
117 extern "C"
118 int xeno_sigwinch_handler(int sig, siginfo_t *si, void *ctxt);
119 #endif
120 namespace OCL
121 {
122  using namespace boost;
123  using namespace std;
124  using namespace RTT;
125  using namespace RTT::detail;
126 #ifdef USE_READLINE
127  std::vector<std::string> TaskBrowser::candidates;
128  std::vector<std::string> TaskBrowser::completes;
129  std::vector<std::string>::iterator TaskBrowser::complete_iter;
130  std::string TaskBrowser::component;
131  std::string TaskBrowser::component_found;
132  std::string TaskBrowser::peerpath;
133  std::string TaskBrowser::text;
134 #endif
135  RTT::TaskContext* TaskBrowser::taskcontext = 0;
136  Service::shared_ptr TaskBrowser::taskobject;
137  RTT::TaskContext* TaskBrowser::peer = 0;
138  RTT::TaskContext* TaskBrowser::tb = 0;
139  RTT::TaskContext* TaskBrowser::context = 0;
140 
141  using boost::bind;
142  using namespace RTT;
143  using namespace std;
144 
145  string TaskBrowser::red;
146  string TaskBrowser::green;
147  string TaskBrowser::blue;
148  std::deque<TaskContext*> taskHistory;
149  std::string TaskBrowser::prompt("> ");
150  std::string TaskBrowser::coloron;
151  std::string TaskBrowser::underline;
152  std::string TaskBrowser::coloroff;
153 
154 
158  static std::ostream&
159  nl(std::ostream& __os)
160  { return __os.put(__os.widen('\n')); }
161 
162  // All readline specific functions
163 #if defined(USE_READLINE)
164 
165  // Signal code only on Posix:
166  int TaskBrowser::rl_received_signal;
167 #if defined(USE_SIGNALS)
168  void TaskBrowser::rl_sigwinch_handler(int sig, siginfo_t *si, void *ctxt) {
169  rl_received_signal = sig;
170 #if defined(OROCOS_TARGET_XENOMAI) && CONFIG_XENO_VERSION_MAJOR == 2 && CONFIG_XENO_VERSION_MINOR >= 5
171  if (xeno_sigwinch_handler(sig, si, ctxt) == 0)
172 #endif
173  rl_resize_terminal();
174  }
175 
176  void TaskBrowser::rl_signal_handler(int sig, siginfo_t *si, void *ctxt) {
177  rl_received_signal = sig;
178  switch(sig) {
179  case SIGINT:
180  if (rl_end > 0) {
181  rl_free_line_state();
182  rl_echo_signal_char(sig);
183  rl_received_signal = 0;
184  }
185  }
186  }
187 #endif // USE_SIGNALS
188 
189  int TaskBrowser::rl_getc(FILE *stream)
190  {
191  int result;
192  unsigned char c;
193 
194  while (1)
195  {
196  rl_received_signal = 0;
197  result = ::read(fileno(stream), &c, sizeof(unsigned char));
198 
199  if (result == sizeof(unsigned char))
200  return (c);
201 
202  /* If zero characters are returned, then the file that we are
203  reading from is empty! Return EOF in that case. */
204  if (result == 0)
205  return (EOF);
206 
207  /* Return an error if SIGINT has been received */
208  if (errno == EINTR && (rl_received_signal == SIGINT || rl_received_signal == SIGTERM))
209  return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
210 
211  /* If the error that we received was EINTR, then try again,
212  this is simply an interrupted system call to read ().
213  Otherwise, some error ocurred, also signifying EOF. */
214  if (errno != EINTR)
215  return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
216  }
217  }
218 
219  char *TaskBrowser::rl_gets ()
220  {
221  /* If the buffer has already been allocated,
222  return the memory to the free pool. */
223  if (line_read)
224  {
225 #ifdef _WIN32
226 
237  free(line_read);
238 #else
239  free (line_read);
240 #endif
241  line_read = 0;
242  }
243 
244  /* Get a line from the user. */
245  std::string p;
246  if ( !macrorecording ) {
247  p = prompt;
248  } else {
249  p = "> ";
250  }
251 
252 #ifdef USE_SIGNALS
253  if (rl_set_signals() != 0)
254  cerr << "Error setting signals !" <<endl;
255 #endif
256 
257  line_read = readline ( p.c_str() );
258 
259 #ifdef USE_SIGNALS
260  if (rl_clear_signals() != 0)
261  cerr << "Error clearing signals !" <<endl;
262 #endif
263 
264  /* If the line has any text in it,
265  save it on the history. */
266  if (line_read && *line_read) {
267  // do not store "quit"
268  string s = line_read;
269  if (s != "quit" && ! ( history_get( where_history() ) && s == string(history_get( where_history() )->line) ) ) {
270 // cout << "Where: " << where_history() << " history_get: " << ( history_get( where_history() ) ? history_get( where_history() )->line : "(null)") << endl;
271 // cout << "History: " << (current_history() ? (const char*) current_history()->line : "(null)") << endl;
272  add_history (line_read);
273  }
274  }
275  return (line_read);
276  }
277 
278  char* TaskBrowser::dupstr( const char *s )
279  {
280  char * rv;
281  // must be C-style :
282  rv = (char*) malloc( strlen( s ) + 1 );
283  strncpy( rv, s, strlen(s) + 1 );
284  return rv;
285  }
286 
287  char *TaskBrowser::command_generator( const char *_text, int state )
288  {
289  // first time called.
290  if ( !state )
291  {
292  // make a copy :
293  text = _text;
294  // rebuild a completion list
295  completes.clear();
296  find_completes();
297  complete_iter = completes.begin();
298  }
299  else
300  ++complete_iter;
301 
302  // return zero if nothing more is found
303  if ( complete_iter == completes.end() )
304  return 0;
305  // return the next completion option
306  return dupstr( complete_iter->c_str() ); // RL requires malloc !
307  }
308 
313  void TaskBrowser::find_completes() {
314  std::string::size_type pos;
315  std::string::size_type startpos;
316  std::string line( rl_line_buffer, rl_point );
317 
318  // complete on 'cd' or 'ls' :
319  if ( line.find(std::string("cd ")) == 0 || line.find(std::string("ls ")) == 0) {
320  //cerr <<endl<< "switch to :" << text<<endl;
321 // pos = text.rfind(".");
322  pos = line.find(" "); // pos+1 is first peername
323  startpos = line.find_last_of(". "); // find last peer
324  //cerr <<"startpos :"<<startpos<<endl;
325  // start searching from component.
326  peer = taskcontext;
327  if ( pos+1 != line.length() ) // bounds check
328  peer = findPeer( line.substr(pos+1) );
329 
330  if (!peer)
331  return;
332  //std::string peername = text.substring( pos+1, std::string::npos );
333  RTT::TaskContext::PeerList v = peer->getPeerList();
334  for (RTT::TaskContext::PeerList::iterator i = v.begin(); i != v.end(); ++i) {
335  std::string path;
336  if ( !( pos+1 > startpos) )
337  path = line.substr(pos+1, startpos - pos);
338  //cerr << "path :"<<path<<endl;
339  if ( *i == line.substr(startpos+1) )
340  completes.push_back( path + *i + ".");
341  else
342  if ( startpos == std::string::npos || startpos+1 == line.length() || i->find( line.substr(startpos+1)) == 0 )
343  completes.push_back( *i );
344  }
345  // Stop here if 'cd'
346  if (line.find(std::string("cd ")) == 0)
347  return;
348  // Add objects for 'ls'.
349  v = peer->provides()->getProviderNames();
350  for (RTT::TaskContext::PeerList::iterator i = v.begin(); i != v.end(); ++i) {
351  std::string path;
352  if ( !( pos+1 > startpos) )
353  path = line.substr(pos+1, startpos - pos);
354  //cerr << "provider:"<< *i << ", path :"<<path<<endl;
355  if ( *i == line.substr(startpos+1) )
356  completes.push_back( path + *i + ".");
357  else
358  if ( startpos == std::string::npos || startpos+1 == line.length() || i->find( line.substr(startpos+1)) == 0 )
359  completes.push_back( *i );
360  }
361  return; // do not add component names.
362  }
363 
364  // TaskBrowser commands :
365  if ( line.find(std::string(".")) == 0 ) {
366  // first make a list of all sensible completions.
367  std::vector<std::string> tbcoms;
368  tbcoms.push_back(".loadProgram ");
369  tbcoms.push_back(".unloadProgram ");
370  tbcoms.push_back(".loadStateMachine ");
371  tbcoms.push_back(".unloadStateMachine ");
372  tbcoms.push_back(".light");
373  tbcoms.push_back(".dark");
374  tbcoms.push_back(".hex");
375  tbcoms.push_back(".nohex");
376  tbcoms.push_back(".nocolors");
377  tbcoms.push_back(".connect");
378  tbcoms.push_back(".record");
379  tbcoms.push_back(".end");
380  tbcoms.push_back(".cancel");
381  tbcoms.push_back(".provide");
382  tbcoms.push_back(".services");
383  tbcoms.push_back(".typekits");
384  tbcoms.push_back(".types");
385 
386  // then see which one matches the already typed line :
387  for( std::vector<std::string>::iterator it = tbcoms.begin();
388  it != tbcoms.end();
389  ++it)
390  if ( it->find(line) == 0 )
391  completes.push_back( *it ); // if partial match, add.
392  return;
393  }
394 
395  if ( line.find(std::string("list ")) == 0
396  || line.find(std::string("trace ")) == 0
397  || line.find(std::string("untrace ")) == 0) {
398  stringstream ss( line.c_str() ); // copy line into ss.
399  string lcommand;
400  ss >> lcommand;
401  lcommand += ' ';
402  std::vector<std::string> progs;
403 
404  if ( context->provides()->hasService("scripting") ) {
405  // THIS:
406  progs = context->getProvider<Scripting>("scripting")->getProgramList();
407  // then see which one matches the already typed line :
408  for( std::vector<std::string>::iterator it = progs.begin();
409  it != progs.end();
410  ++it) {
411  string res = lcommand + *it;
412  if ( res.find(line) == 0 )
413  completes.push_back( *it ); // if partial match, add.
414  }
415  progs = context->getProvider<Scripting>("scripting")->getStateMachineList();
416  for( std::vector<std::string>::iterator it = progs.begin();
417  it != progs.end();
418  ++it) {
419  string res = lcommand + *it;
420  if ( res.find(line) == 0 )
421  completes.push_back( *it ); // if partial match, add.
422  }
423  }
424  return;
425  }
426 
427  startpos = text.find_last_of(",( ");
428  if ( startpos == std::string::npos )
429  startpos = 0; // if none is found, start at beginning
430 
431  // complete on peers and objects, and find the peer the user wants completion for
432  find_peers(startpos);
433  // now proceed with 'this->peer' as TC,
434  // this->taskobject as TO and
435  // this->component as object and
436  // this->peerpath as the text leading up to 'this->component'.
437 
438  // store the partial compname or peername
439  std::string comp = component;
440 
441 
442  // NEXT: use taskobject to complete commands, events, methods, attrs
443  // based on 'component' (find trick).
444  // if taskobject == peer, also complete properties
445  find_ops();
446 
447  // TODO: concat two cases below as text.find("cd")...
448  // check if the user is tabbing on an empty command, then add the console commands :
449  if ( line.empty() ) {
450  completes.push_back("cd ");
451  completes.push_back("cd ..");
452  completes.push_back("ls");
453  completes.push_back("help");
454  completes.push_back("quit");
455  completes.push_back("list");
456  completes.push_back("trace");
457  completes.push_back("untrace");
458  if (taskcontext == context)
459  completes.push_back("leave");
460  else
461  completes.push_back("enter");
462  // go on below to add components and tasks as well.
463  }
464 
465  // only try this if text is not empty.
466  if ( !text.empty() ) {
467  if ( std::string( "cd " ).find(text) == 0 )
468  completes.push_back("cd ");
469  if ( std::string( "ls" ).find(text) == 0 )
470  completes.push_back("ls");
471  if ( std::string( "cd .." ).find(text) == 0 )
472  completes.push_back("cd ..");
473  if ( std::string( "help" ).find(text) == 0 )
474  completes.push_back("help");
475  if ( std::string( "quit" ).find(text) == 0 )
476  completes.push_back("quit");
477  if ( std::string( "list " ).find(text) == 0 )
478  completes.push_back("list ");
479  if ( std::string( "trace " ).find(text) == 0 )
480  completes.push_back("trace ");
481  if ( std::string( "untrace " ).find(text) == 0 )
482  completes.push_back("untrace ");
483 
484  if (taskcontext == context && string("leave").find(text) == 0)
485  completes.push_back("leave");
486 
487  if (context == tb && string("enter").find(text) == 0)
488  completes.push_back("enter");
489  }
490  }
491 
492  void TaskBrowser::find_ops()
493  {
494  // the last (incomplete) text is stored in 'component'.
495  // all attributes :
496  std::vector<std::string> attrs;
497  attrs = taskobject->getAttributeNames();
498  for (std::vector<std::string>::iterator i = attrs.begin(); i!= attrs.end(); ++i ) {
499  if ( i->find( component ) == 0 )
500  completes.push_back( peerpath + *i );
501  }
502  // all properties if RTT::TaskContext/Service:
503  std::vector<std::string> props;
504  taskobject->properties()->list(props);
505  for (std::vector<std::string>::iterator i = props.begin(); i!= props.end(); ++i ) {
506  if ( i->find( component ) == 0 ) {
507  completes.push_back( peerpath + *i );
508  }
509  }
510 
511  // methods:
512  vector<string> comps = taskobject->getNames();
513  for (std::vector<std::string>::iterator i = comps.begin(); i!= comps.end(); ++i ) {
514  if ( i->find( component ) == 0 )
515  completes.push_back( peerpath + *i );
516  }
517 
518  // types:
519  comps = Types()->getDottedTypes();
520  for (std::vector<std::string>::iterator i = comps.begin(); i!= comps.end(); ++i ) {
521  if ( peerpath.empty() && i->find( component ) == 0 )
522  completes.push_back( *i );
523  }
524 
525  // Global Attributes:
526  comps = GlobalsRepository::Instance()->getAttributeNames();
527  for (std::vector<std::string>::iterator i = comps.begin(); i!= comps.end(); ++i ) {
528  if ( peerpath.empty() && i->find( component ) == 0 )
529  completes.push_back( *i );
530  }
531 
532  // Global methods:
533  if ( taskobject == peer->provides() && peer == context) {
534  comps = GlobalService::Instance()->getNames();
535  for (std::vector<std::string>::iterator i = comps.begin(); i!= comps.end(); ++i ) {
536  if ( i->find( component ) == 0 )
537  completes.push_back( peerpath + *i );
538  }
539  }
540 
541  // complete on types:
542  bool try_deeper = false;
543  try {
544  Parser parser(GlobalEngine::Instance());
545  DataSourceBase::shared_ptr result = parser.parseExpression( peerpath + component_found, context );
546  if (result && !component.empty() ) {
547  DataSource<PropertyBag>::shared_ptr bag = DataSource<PropertyBag>::narrow(result.get());
548  vector<string> members;
549  if(bag){
550  members = bag->rvalue().getPropertyNames();
551  }
552  else{
553  members = result->getMemberNames();
554  }
555  for (std::vector<std::string>::iterator i = members.begin(); i!= members.end(); ++i ) {
556  if ( string( component_found + "." + *i ).find( component ) == 0 )
557  completes.push_back( peerpath + component_found + "." + *i );
558  if ( component_found + "." + *i == component )
559  try_deeper = true;
560  }
561  }
562  } catch(...) {}
563  // this is a hack to initiate a complete on a valid expression that might have members.
564  // the completer above would only return the expression itself, while this one tries to
565  // go a level deeper again.
566  if (try_deeper) {
567  try {
568  Parser parser(GlobalEngine::Instance());
569  DataSourceBase::shared_ptr result = parser.parseExpression( peerpath + component, context );
570  if (result && !component.empty() ) {
571  DataSource<PropertyBag>::shared_ptr bag = DataSource<PropertyBag>::narrow(result.get());
572  vector<string> members;
573  if(bag){
574  members = bag->rvalue().getPropertyNames();
575  }
576  else{
577  members = result->getMemberNames();
578  }
579  for (std::vector<std::string>::iterator i = members.begin(); i!= members.end(); ++i ) {
580  if (component_found + "." != component ) // catch corner case.
581  completes.push_back( peerpath + component + "." + *i );
582  }
583  }
584  } catch(...) {}
585  }
586  }
587 
588  void TaskBrowser::find_peers( std::string::size_type startpos )
589  {
590  peerpath.clear();
591  peer = context;
592  taskobject = context->provides();
593 
594  std::string to_parse = text.substr(startpos);
595  startpos = 0;
596  std::string::size_type endpos = 0;
597  // Traverse the entered peer-list
598  component.clear();
599  peerpath.clear();
600  // This loop separates the peer/service from the member/method
601  while (endpos != std::string::npos )
602  {
603  bool itemfound = false;
604  endpos = to_parse.find(".");
605  if ( endpos == startpos ) {
606  component.clear();
607  break;
608  }
609  std::string item = to_parse.substr(startpos, endpos);
610 
611  if ( taskobject->hasService( item ) ) {
612  taskobject = taskobject->provides(item);
613  itemfound = true;
614  } else
615  if ( peer->hasPeer( item ) ) {
616  peer = peer->getPeer( item );
617  taskobject = peer->provides();
618  itemfound = true;
619  } else if ( GlobalService::Instance()->hasService(item) ) {
620  taskobject = GlobalService::Instance()->provides(item);
621  itemfound = true;
622  }
623  if ( itemfound ) { // if "." found and correct path
624  peerpath += to_parse.substr(startpos, endpos) + ".";
625  if ( endpos != std::string::npos )
626  to_parse = to_parse.substr(endpos + 1);
627  else
628  to_parse.clear();
629  startpos = 0;
630  }
631  else {
632  // no match: typo or member name
633  // store the text until the last dot:
634  component_found = to_parse.substr(startpos, to_parse.rfind("."));
635  // store the complete text
636  component = to_parse.substr(startpos, std::string::npos);
637  break;
638  }
639  }
640 
641  // now we got the peer and taskobject pointers,
642  // the completed path in peerpath
643  // the last partial path in component
644 // cout << "text: '" << text <<"'"<<endl;
645 // cout << "to_parse: '" << text <<"'"<<endl;
646 // cout << "Peerpath: '" << peerpath <<"'"<<endl;
647  // cout <<endl<< "Component: '" << component <<"'"<<endl;
648  // cout << "Component_found: '" << component_found <<"'"<<endl;
649 
650  RTT::TaskContext::PeerList v;
651  if ( taskobject == peer->provides() ) {
652  // add peer's completes:
653  v = peer->getPeerList();
654  for (RTT::TaskContext::PeerList::iterator i = v.begin(); i != v.end(); ++i) {
655  if ( i->find( component ) == 0 ) { // only add if match
656  completes.push_back( peerpath + *i );
657  completes.push_back( peerpath + *i + "." );
658  //cerr << "added " << peerpath+*i+"."<<endl;
659  }
660  }
661  }
662  // add taskobject's completes:
663  v = taskobject->getProviderNames();
664  for (RTT::TaskContext::PeerList::iterator i = v.begin(); i != v.end(); ++i) {
665  if ( i->find( component ) == 0 ) { // only add if match
666  completes.push_back( peerpath + *i );
667  if ( *i != "this" ) // "this." confuses our parsing lateron
668  completes.push_back( peerpath + *i + "." );
669  //cerr << "added " << peerpath+*i+"."<<endl;
670  }
671  }
672  // add global service completes:
673  if ( peer == context && taskobject == peer->provides() ) {
674  v = GlobalService::Instance()->getProviderNames();
675  for (RTT::TaskContext::PeerList::iterator i = v.begin(); i != v.end(); ++i) {
676  if ( i->find( component ) == 0 ) { // only add if match
677  completes.push_back( peerpath + *i );
678  if ( *i != "this" ) // "this." confuses our parsing lateron
679  completes.push_back( peerpath + *i + "." );
680  //cerr << "added " << peerpath+*i+"."<<endl;
681  }
682  }
683  }
684  return;
685  }
686 
687  char ** TaskBrowser::orocos_hmi_completion ( const char *text, int start, int end )
688  {
689  char **matches;
690  matches = ( char ** ) 0;
691 
692  matches = rl_completion_matches ( text, &TaskBrowser::command_generator );
693 
694  return ( matches );
695  }
696 #endif // USE_READLINE
697 
698  TaskBrowser::TaskBrowser( RTT::TaskContext* _c )
699  : RTT::TaskContext("TaskBrowser"),
700  debug(0),
701  line_read(0),
702  lastc(0), storedname(""), storedline(-1),
703  usehex(false),
704  histfile(0),
705  macrorecording(false)
706  {
707  tb = this;
708  context = tb;
709  this->switchTaskContext(_c);
710 #ifdef USE_READLINE
711  // we always catch sigwinch ourselves, in order to pass it on to Xenomai if necessary.
712 #ifdef USE_SIGNALS
713  rl_catch_sigwinch = 0;
714  rl_catch_signals = 0;
715 #endif
716  rl_completion_append_character = '\0'; // avoid adding spaces
717  rl_attempted_completion_function = &TaskBrowser::orocos_hmi_completion;
718  rl_getc_function = &TaskBrowser::rl_getc;
719 
720  using_history();
721  histfile = getenv("ORO_TB_HISTFILE");
722  if(histfile == 0)
723  histfile = ".tb_history";
724  if ( read_history(histfile) != 0 ) {
725  read_history("~/.tb_history");
726  }
727 #ifdef USE_SIGNALS
728  struct sigaction sa;
729  sa.sa_sigaction = &TaskBrowser::rl_sigwinch_handler;
730  sa.sa_flags = SA_SIGINFO | SA_RESTART;
731  sigemptyset( &sa.sa_mask );
732  sigaction(SIGWINCH, &sa, 0);
733 
734  sa.sa_sigaction = &(TaskBrowser::rl_signal_handler);
735  sa.sa_flags = SA_SIGINFO;
736  sigaction(SIGINT, &sa, 0);
737  sigaction(SIGTERM, &sa, 0);
738 #endif // USE_SIGNALS
739 #endif // USE_READLINE
740 
741  this->setColorTheme( darkbg );
742  this->enterTask();
743  }
744 
745  TaskBrowser::~TaskBrowser() {
746 #ifdef USE_READLINE
747  if (line_read)
748  {
749  free (line_read);
750  }
751  if ( write_history(histfile) != 0 ) {
752  write_history("~/.tb_history");
753  }
754 #endif
755  }
756 
760  char getTaskStatusChar(RTT::TaskContext* t)
761  {
762  if (t->inFatalError())
763  return 'F';
764  if (t->inRunTimeError())
765  return 'E';
766  if (t->inException())
767  return 'X';
768  if (t->isRunning() )
769  return 'R'; // Running
770  if (t->isConfigured() )
771  return 'S'; // Stopped
772  return 'U'; // Unconfigured/Preoperational
773  }
774 
775  char getStateMachineStatusChar(RTT::TaskContext* t, string progname)
776  {
777  string ps = t->getProvider<Scripting>("scripting")->getStateMachineStatusStr(progname);
778  return toupper(ps[0]);
779  }
780 
781  char getProgramStatusChar(RTT::TaskContext* t, string progname)
782  {
783  string ps = t->getProvider<Scripting>("scripting")->getProgramStatusStr(progname);
784  return toupper(ps[0]);
785  }
786 
787  void str_trim(string& str, char to_trim)
788  {
789  string::size_type pos1 = str.find_first_not_of(to_trim);
790  string::size_type pos2 = str.find_last_not_of(to_trim);
791  if (pos1 == string::npos)
792  str.clear(); // nothing else present
793  else
794  str = str.substr(pos1, pos2 - pos1 + 1);
795  }
796 
797 
803  {
804  cout << nl<<
805  coloron <<
806  " This console reader allows you to browse and manipulate TaskContexts."<<nl<<
807  " You can type in an operation, expression, create or change variables."<<nl;
808  cout <<" (type '"<<underline<<"help"<<coloroff<<coloron<<"' for instructions and '"
809  <<underline<<"ls"<<coloroff<<coloron<<"' for context info)"<<nl<<nl;
810 #ifdef USE_READLINE
811  cout << " TAB completion and HISTORY is available ('bash' like)" <<nl<<nl;
812 #else
813  cout << " TAB completion and history is NOT available (LGPL-version)" <<nl<<nl;
814 #endif
815  cout << " Use '"<<underline<<"Ctrl-D"<<coloroff<<coloron<<"' or type '"<<underline<<"quit"<<coloroff<<coloron<<"' to exit this program." <<coloroff<<nl<<nl;
816 
817  while (1) {
818  try {
819  if (!macrorecording) {
820  if ( context == tb )
821  cout << green << " Watching " <<coloroff;
822 
823  char state = getTaskStatusChar(taskcontext);
824 
825  // sets prompt for readline:
826 // prompt = green + taskcontext->getName() + coloroff + "[" + state + "]> ";
827  prompt = taskcontext->getName() + " [" + state + "]> ";
828  // This 'endl' is important because it flushes the whole output to screen of all
829  // processing that previously happened, which was using 'nl'.
830  cout.flush();
831 
832  // print traces.
833  for (PTrace::iterator it = ptraces.begin(); it != ptraces.end(); ++it) {
834  RTT::TaskContext* progpeer = it->first.first;
835  int line = progpeer->getProvider<Scripting>("scripting")->getProgramLine(it->first.second);
836  if ( line != it->second ) {
837  it->second = line;
838  printProgram( it->first.second, -1, progpeer );
839  }
840  }
841 
842  for (PTrace::iterator it = straces.begin(); it != straces.end(); ++it) {
843  RTT::TaskContext* progpeer = it->first.first;
844  int line = progpeer->getProvider<Scripting>("scripting")->getStateMachineLine(it->first.second);
845  if ( line != it->second ) {
846  it->second = line;
847  printProgram( it->first.second, -1, progpeer );
848  }
849  }
850  }
851  // Check port status:
852  checkPorts();
853  std::string command;
854  // When using rxvt on windows, the process will receive signals when the arrow keys are used
855  // during input. We compile with /EHa to catch these signals and don't print anything.
856  try {
857 #ifdef USE_READLINE
858  const char* const commandStr = rl_gets();
859  // quit on EOF (Ctrl-D)
860  command = commandStr ? commandStr : "quit"; // copy over to string
861 #else
862  cout << prompt;
863  getline(cin,command);
864  if (!cin) // Ctrl-D
865  command = "quit";
866 #endif
867  } catch(std::exception& e) {
868  cerr << "The command line reader throwed a std::exception: '"<< e.what()<<"'."<<endl;
869  } catch (...) {
870  cerr << "The command line reader throwed an exception." <<endlog();
871  }
872  str_trim( command, ' ');
873  cout << coloroff;
874  if ( command == "quit" ) {
875  // Intercept no Ctrl-C
876  cout << endl;
877  return;
878  } else if ( command == "help") {
879  printHelp();
880  } else if ( command.find("help ") == 0) {
881  printHelp( command.substr(command.rfind(' ')));
882  } else if ( command == "#debug") {
883  debug = !debug;
884  } else if ( command.find("list ") == 0 || command == "list" ) {
885  browserAction(command);
886  } else if ( command.find("trace ") == 0 || command == "trace" ) {
887  browserAction(command);
888  } else if ( command.find("untrace ") == 0 || command == "untrace" ) {
889  browserAction(command);
890  } else if ( command.find("ls") == 0 ) {
891  std::string::size_type pos = command.find("ls")+2;
892  command = std::string(command, pos, command.length());
893  str_trim( command, ' ');
894  printInfo( command );
895  } else if ( command == "" ) { // nop
896  } else if ( command.find("cd ..") == 0 ) {
897  this->switchBack( );
898  } else if ( command.find("enter") == 0 ) {
899  this->enterTask();
900  } else if ( command.find("leave") == 0 ) {
901  this->leaveTask();
902  } else if ( command.find("cd ") == 0 ) {
903  std::string::size_type pos = command.find("cd")+2;
904  command = std::string(command, pos, command.length());
905  this->switchTaskContext( command );
906  } else if ( command.find(".") == 0 ) {
907  command = std::string(command, 1, command.length());
908  this->browserAction( command );
909  } else if ( macrorecording) {
910  macrotext += command +'\n';
911  } else {
912  try {
913  this->evalCommand( command );
914  } catch(std::exception& e) {
915  cerr << "The command '"<<command<<"' caused a std::exception: '"<< e.what()<<"' and could not be completed."<<endl;
916  } catch(...){
917  cerr << "The command '"<<command<<"' caused an unknown exception and could not be completed."<<endl;
918  }
919  // a command was typed... clear storedline such that a next 'list'
920  // shows the 'IP' again.
921  storedline = -1;
922  }
923  //cout <<endl;
924  } catch(std::exception& e) {
925  cerr << "Warning: The command caused a std::exception: '"<< e.what()<<"' in the TaskBrowser's loop() function."<<endl;
926  } catch(...) {
927  cerr << "Warning: The command caused an exception in the TaskBrowser's loop() function." << endl;
928  }
929  }
930  }
931 
932  void TaskBrowser::enterTask()
933  {
934  if ( context == taskcontext ) {
935  log(Info) <<"Already in Task "<< taskcontext->getName()<<endlog();
936  return;
937  }
938  context = taskcontext;
939  log(Info) <<"Entering Task "<< taskcontext->getName()<<endlog();
940  }
941 
942  void TaskBrowser::leaveTask()
943  {
944  if ( context == tb ) {
945  log(Info) <<"Already watching Task "<< taskcontext->getName()<<endlog();
946  return;
947  }
948  context = tb;
949  log(Info) <<"Watching Task "<< taskcontext->getName()<<endlog();
950  }
951 
952  void TaskBrowser::recordMacro(std::string name)
953  {
954  if (macrorecording) {
955  log(Error)<< "Macro already active." <<endlog();
956  return;
957  }
958  if (context->provides()->hasService("scripting") == false) {
959  log(Error)<< "Can not create a macro in a TaskContext without scripting service." <<endlog();
960  return;
961  }
962  if ( name.empty() ) {
963  cerr << "Please specify a macro name." <<endl;
964  return;
965  } else {
966  cout << "Recording macro "<< name <<endl;
967  cout << "Use program scripting syntax (do, set,...) !" << endl <<endl;
968  cout << "export function "<< name<<" {"<<endl;
969  }
970  macrorecording = true;
971  macroname = name;
972  }
973 
974  void TaskBrowser::cancelMacro() {
975  if (!macrorecording) {
976  log(Warning)<< "Macro recording was not active." <<endlog();
977  return;
978  }
979  cout << "Canceling macro "<< macroname <<endl;
980  macrorecording = false;
981  macrotext.clear();
982  }
983 
984  void TaskBrowser::endMacro() {
985  if (!macrorecording) {
986  log(Warning)<< "Macro recording was not active." <<endlog();
987  return;
988  }
989  string fname = macroname + ".ops";
990  macrorecording = false;
991  cout << "}" <<endl;
992  cout << "Saving file "<< fname <<endl;
993  ofstream macrofile( fname.c_str() );
994  macrofile << "/* TaskBrowser macro '"<<macroname<<"' */" <<endl<<endl;
995  macrofile << "export function "<<macroname<<" {"<<endl;
996  macrofile << macrotext.c_str();
997  macrofile << "}"<<endl;
998  macrotext.clear();
999 
1000  cout << "Loading file "<< fname <<endl;
1001  context->getProvider<Scripting>("Scripting")->loadPrograms(fname);
1002  }
1003 
1005  {
1006  if ( taskHistory.size() == 0)
1007  return;
1008 
1009  this->switchTaskContext( taskHistory.front(), false ); // store==false
1010  lastc = 0;
1011  taskHistory.pop_front();
1012  }
1013 
1014  void TaskBrowser::checkPorts()
1015  {
1016  // check periodically if the taskcontext did not change its ports.
1017 
1018  DataFlowInterface::Ports ports;
1019  ports = this->ports()->getPorts();
1020  for( DataFlowInterface::Ports::iterator i=ports.begin(); i != ports.end(); ++i) {
1021  // If our port is no longer connected, try to reconnect.
1022  base::PortInterface* p = *i;
1023  base::PortInterface* tcp = taskcontext->ports()->getPort( p->getName() );
1024  if ( p->connected() == false || tcp == 0 || tcp->connected() == false) {
1025  this->ports()->removePort( p->getName() );
1026  delete p;
1027  }
1028  }
1029  }
1030 
1031  void TaskBrowser::setColorTheme(ColorTheme t)
1032  {
1033  // background color palettes:
1034  const char* dbg = "\033[01;";
1035  const char* wbg = "\033[02;";
1036  // colors in palettes:
1037  const char* r = "31m";
1038  const char* g = "32m";
1039  const char* b = "34m";
1040  const char* con = "31m";
1041  const char* coff = "\33[0m";
1042  const char* und = "\33[4m";
1043 
1044  switch (t)
1045  {
1046  case nocolors:
1047  green.clear();
1048  red.clear();
1049  blue.clear();
1050  coloron.clear();
1051  coloroff.clear();
1052  underline.clear();
1053  return;
1054  break;
1055  case darkbg:
1056  green = dbg;
1057  red = dbg;
1058  blue = dbg;
1059  coloron = dbg;
1060  coloroff = wbg;
1061  break;
1062  case whitebg:
1063  green = wbg;
1064  red = wbg;
1065  blue = wbg;
1066  coloron = wbg;
1067  coloroff = wbg;
1068  break;
1069  }
1070  green += g;
1071  red += r;
1072  blue += b;
1073  coloron += con;
1074  coloroff = coff;
1075  underline = und;
1076  }
1077 
1078  void TaskBrowser::switchTaskContext(std::string& c) {
1079  // if nothing new found, return.
1080  peer = taskcontext;
1081  if ( this->findPeer( c + "." ) == 0 ) {
1082  cerr << "No such peer: "<< c <<nl;
1083  return;
1084  }
1085 
1086  if ( peer == taskcontext ) {
1087  cerr << "Already in "<< c <<nl;
1088  return;
1089  }
1090 
1091  if ( peer == tb ) {
1092  cerr << "Can not switch to TaskBrowser." <<nl;
1093  return;
1094  }
1095 
1096  // findPeer has set 'peer' :
1097  this->switchTaskContext( peer );
1098  }
1099 
1100  void TaskBrowser::switchTaskContext(RTT::TaskContext* tc, bool store) {
1101  // put current on the stack :
1102  if (taskHistory.size() == 20 )
1103  taskHistory.pop_back();
1104  if ( taskcontext && store)
1105  taskHistory.push_front( taskcontext );
1106 
1107  // disconnect from current peers.
1108  this->disconnect();
1109 
1110  // cleanup port left-overs.
1111  DataFlowInterface::Ports tports = this->ports()->getPorts();
1112  for( DataFlowInterface::Ports::iterator i=tports.begin(); i != tports.end(); ++i) {
1113  this->ports()->removePort( (*i)->getName() );
1114  delete *i;
1115  }
1116 
1117  // now switch to new one :
1118  if ( context == taskcontext )
1119  context = tc;
1120  taskcontext = tc; // peer is the new taskcontext.
1121  lastc = 0;
1122 
1123  // connect peer.
1124  this->addPeer( taskcontext );
1125 
1126  // map data ports.
1127  // create 'anti-ports' to allow port-level interaction with the peer.
1128  tports = taskcontext->ports()->getPorts();
1129  if ( !tports.empty() )
1130  cout <<nl << "TaskBrowser connects to all data ports of "<<taskcontext->getName()<<endl;
1131  for( DataFlowInterface::Ports::iterator i=tports.begin(); i != tports.end(); ++i) {
1132  if (this->ports()->getPort( (*i)->getName() ) == 0 )
1133  this->ports()->addPort( *(*i)->antiClone() );
1134  }
1135  RTT::connectPorts(this,taskcontext);
1136 
1137 
1138 
1139  cerr << " Switched to : " << taskcontext->getName() <<endl;
1140 
1141  }
1142 
1143  RTT::TaskContext* TaskBrowser::findPeer(std::string c) {
1144  // returns the one but last peer, which is the one we want.
1145  std::string s( c );
1146 
1147  our_pos_iter_t parsebegin( s.begin(), s.end(), "teststring" );
1148  our_pos_iter_t parseend;
1149 
1150  CommonParser cp;
1151  scripting::PeerParser pp( peer, cp, true );
1152  bool skipref = true;
1153  try {
1154  parse( parsebegin, parseend, pp.parser(), SKIP_PARSER );
1155  }
1156  catch( ... )
1157  {
1158  log(Debug) <<"No such peer : "<< c <<endlog();
1159  return 0;
1160  }
1161  taskobject = pp.taskObject();
1162  peer = pp.peer();
1163  return pp.peer();
1164  }
1165 
1166  void TaskBrowser::browserAction(std::string& act)
1167  {
1168  std::stringstream ss(act);
1169  std::string instr;
1170  ss >> instr;
1171 
1172  if ( instr == "list" ) {
1173  if (context->provides()->hasService("scripting") == false) {
1174  log(Error)<< "Can not list a program in a TaskContext without scripting service." <<endlog();
1175  return;
1176  }
1177  int line;
1178  ss >> line;
1179  if (ss) {
1180  this->printProgram(line);
1181  return;
1182  }
1183  ss.clear();
1184  string arg;
1185  ss >> arg;
1186  if (ss) {
1187  ss.clear();
1188  ss >> line;
1189  if (ss) {
1190  // progname and line given
1191  this->printProgram(arg, line);
1192  return;
1193  }
1194  // only progname given.
1195  this->printProgram( arg );
1196  return;
1197  }
1198  // just 'list' :
1199  this->printProgram();
1200  return;
1201  }
1202 
1203  //
1204  // TRACING
1205  //
1206  if ( instr == "trace") {
1207  if (context->provides()->hasService("scripting") == false) {
1208  log(Error)<< "Can not trace a program in a TaskContext without scripting service." <<endlog();
1209  return;
1210  }
1211 
1212  string arg;
1213  ss >> arg;
1214  if (ss) {
1215  bool pi = context->getProvider<Scripting>("scripting")->hasProgram(arg);
1216  if (pi) {
1217  ptraces[make_pair(context, arg)] = context->getProvider<Scripting>("scripting")->getProgramLine(arg); // store current line number.
1218  this->printProgram( arg );
1219  return;
1220  }
1221  pi = context->getProvider<Scripting>("scripting")->hasStateMachine(arg);
1222  if (pi) {
1223  straces[make_pair(context, arg)] = context->getProvider<Scripting>("scripting")->getStateMachineLine(arg); // store current line number.
1224  this->printProgram( arg );
1225  return;
1226  }
1227  cerr <<"No such program or state machine: "<< arg <<endl;
1228  return;
1229  }
1230 
1231  // just 'trace' :
1232  std::vector<std::string> names;
1233  names = context->getProvider<Scripting>("scripting")->getProgramList();
1234  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1235  bool pi = context->getProvider<Scripting>("scripting")->hasProgram(arg);
1236  if (pi)
1237  ptraces[make_pair(context, arg)] = context->getProvider<Scripting>("scripting")->getProgramLine(arg); // store current line number.
1238  }
1239 
1240  names = context->getProvider<Scripting>("scripting")->getStateMachineList();
1241  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1242  bool pi = context->getProvider<Scripting>("scripting")->hasStateMachine(arg);
1243  if (pi)
1244  straces[make_pair(context, arg)] = context->getProvider<Scripting>("scripting")->getStateMachineLine(arg); // store current line number.
1245  }
1246 
1247  cerr << "Tracing all programs and state machines in "<< context->getName() << endl;
1248  return;
1249  }
1250 
1251  if ( instr == "untrace") {
1252  if (context->provides()->hasService("scripting") == false) {
1253  log(Error)<< "Can not untrace a program in a TaskContext without scripting service." <<endlog();
1254  return;
1255  }
1256  string arg;
1257  ss >> arg;
1258  if (ss) {
1259  ptraces.erase( make_pair(context, arg) );
1260  straces.erase( make_pair(context, arg) );
1261  cerr <<"Untracing "<< arg <<" of "<< context->getName()<<endl;
1262  return;
1263  }
1264  // just 'untrace' :
1265  std::vector<std::string> names;
1266  names = context->getProvider<Scripting>("scripting")->getProgramList();
1267  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1268  bool pi = context->getProvider<Scripting>("scripting")->hasProgram(arg);
1269  if (pi)
1270  ptraces.erase(make_pair(context, arg));
1271  }
1272 
1273  names = context->getProvider<Scripting>("scripting")->getStateMachineList();
1274  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1275  bool pi = context->getProvider<Scripting>("scripting")->hasStateMachine(arg);
1276  if (pi)
1277  straces.erase(make_pair(context, arg));
1278  }
1279 
1280  cerr << "Untracing all programs and state machines of "<< context->getName() << endl;
1281  return;
1282  }
1283 
1284  std::string arg;
1285  ss >> arg;
1286  if ( instr == "dark") {
1287  this->setColorTheme(darkbg);
1288  cout << nl << "Setting Color Theme for "+green+"dark"+coloroff+" backgrounds."<<endl;
1289  return;
1290  }
1291  if ( instr == "light") {
1292  this->setColorTheme(whitebg);
1293  cout << nl << "Setting Color Theme for "+green+"light"+coloroff+" backgrounds."<<endl;
1294  return;
1295  }
1296  if ( instr == "nocolors") {
1297  this->setColorTheme(nocolors);
1298  cout <<nl << "Disabling all colors"<<endl;
1299  return;
1300  }
1301  if ( instr == "record") {
1302  recordMacro( arg );
1303  return;
1304  }
1305  if ( instr == "cancel") {
1306  cancelMacro();
1307  return;
1308  }
1309  if ( instr == "end") {
1310  endMacro();
1311  return;
1312  }
1313  if ( instr == "hex") {
1314  usehex = true;
1315  cout << "Switching to hex notation for output (use .nohex to revert)." <<endl;
1316  return;
1317  }
1318  if ( instr == "nohex") {
1319  usehex = false;
1320  cout << "Turning off hex notation for output." <<endl;
1321  return;
1322  }
1323  if ( instr == "provide") {
1324  while ( ss ) {
1325  cout << "Trying to locate service '" << arg << "'..."<<endl;
1326  if ( PluginLoader::Instance()->loadService(arg, context) )
1327  cout << "Service '"<< arg << "' loaded in " << context->getName() << endl;
1328  else
1329  cout << "Service not found." <<endl;
1330  ss >> arg;
1331  }
1332  return;
1333  }
1334  if (instr == "services") {
1335  vector<string> names = PluginLoader::Instance()->listServices();
1336  cout << "Available Services: ";
1337  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1338  cout << " " << *it;
1339  }
1340  cout <<endl;
1341  return;
1342  }
1343  if (instr == "typekits") {
1344  vector<string> names = PluginLoader::Instance()->listTypekits();
1345  cout << "Available Typekits: ";
1346  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1347  cout << " " << *it;
1348  }
1349  cout <<endl;
1350  return;
1351  }
1352  if (instr == "types") {
1353  vector<string> names = TypeInfoRepository::Instance()->getDottedTypes();
1354  cout << "Available data types: ";
1355  for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
1356  cout << " " << *it;
1357  }
1358  cout <<endl;
1359  return;
1360  }
1361  cerr << "Unknown Browser Action : "<< act <<endl;
1362  cerr << "See 'help' for valid syntax."<<endl;
1363  }
1364 
1365  void TaskBrowser::evaluate(std::string& comm) {
1366  this->evalCommand(comm);
1367  }
1368 
1369  Service::shared_ptr TaskBrowser::stringToService(string const& names) {
1370  Service::shared_ptr serv;
1371  std::vector<std::string> strs;
1372  boost::split(strs, names, boost::is_any_of("."));
1373 
1374  // strs could be empty because of a bug in Boost 1.44 (see https://svn.boost.org/trac/boost/ticket/4751)
1375  if (strs.empty()) return serv;
1376 
1377  string component = strs.front();
1378  if (! context->hasPeer(component) && !context->provides()->hasService(component) ) {
1379  return serv;
1380  }
1381  // We only support help for peer or subservice:
1382  if ( context->hasPeer(component) )
1383  serv = context->getPeer(component)->provides();
1384  else if (context->provides()->hasService(component))
1385  serv = context->provides(component);
1386 
1387  // remove component name:
1388  strs.erase( strs.begin() );
1389 
1390  // iterate over remainders:
1391  while ( !strs.empty() && serv) {
1392  serv = serv->getService( strs.front() );
1393  if (serv)
1394  strs.erase( strs.begin() );
1395  }
1396  return serv;
1397  }
1398 
1399 
1400 
1401  bool TaskBrowser::printService( string name ) {
1402  bool result = false;
1403  Service::shared_ptr ops = stringToService(name);
1404  ServiceRequester::shared_ptr sr;
1405 
1406  if ( ops || GlobalService::Instance()->hasService( name ) ) // only object name was typed
1407  {
1408  if ( !ops )
1409  ops = GlobalService::Instance()->provides(name);
1410  sresult << nl << "Printing Interface of '"<< coloron << ops->getName() <<coloroff <<"' :"<<nl<<nl;
1411  vector<string> methods = ops->getNames();
1412  std::for_each( methods.begin(), methods.end(), boost::bind(&TaskBrowser::printOperation, this, _1, ops) );
1413  cout << sresult.str();
1414  sresult.str("");
1415  result = true;
1416  }
1417  if ( context->requires()->requiresService( name ) ) // only object name was typed
1418  {
1419  sr = context->requires(name);
1420  sresult << nl << "Requiring '"<< coloron << sr->getRequestName() <<coloroff <<"' with methods: ";
1421  vector<string> methods = sr->getOperationCallerNames();
1422  sresult << coloron;
1423  std::for_each( methods.begin(), methods.end(), sresult << lambda::_1 <<" " );
1424  cout << sresult.str() << coloroff << nl;
1425  sresult.str("");
1426  result = true;
1427  }
1428  return result;
1429  }
1430 
1431  void TaskBrowser::evalCommand(std::string& comm )
1432  {
1433  // deprecated: use 'help servicename'
1434  bool result = printService(comm);
1435 
1436  // Minor hack : also check if it was an attribute of current TC, for example,
1437  // if both the object and attribute with that name exist. the if
1438  // statement after this one would return and not give the expr parser
1439  // time to evaluate 'comm'.
1440  if ( context->provides()->getValue( comm ) ) {
1441  if (debug)
1442  cerr << "Found value..."<<nl;
1443  this->printResult( context->provides()->getValue( comm )->getDataSource().get(), true );
1444  cout << sresult.str()<<nl;
1445  sresult.str("");
1446  return;
1447  }
1448 
1449  if ( result ) {
1450  return;
1451  }
1452 
1453  // Set caller=0 to have correct call/send semantics.
1454  // we're outside the updateHook(). Passing 'this' would
1455  // trigger the EE of the TB, but not our own function.
1456  scripting::Parser _parser( GlobalEngine::Instance() );
1457 
1458  if (debug)
1459  cerr << "Trying ValueStatement..."<<nl;
1460  try {
1461  // Check if it was a method or datasource :
1462  last_expr = _parser.parseValueStatement( comm, context );
1463  // methods and DS'es are processed immediately.
1464  if ( last_expr ) {
1465  // only print if no ';' was given.
1466  assert( comm.size() != 0 );
1467  if ( comm[ comm.size() - 1 ] != ';' ) {
1468  this->printResult( last_expr.get(), true );
1469  cout << sresult.str() << nl <<endl;
1470  sresult.str("");
1471  } else
1472  last_expr->evaluate();
1473  return; // done here
1474  } else if (debug)
1475  cerr << "returned (null) !"<<nl;
1476  //cout << " (ok)" <<nl;
1477  //return; //
1478  } catch ( fatal_semantic_parse_exception& pe ) { // incorr args, ...
1479  // way to fatal, must be reported immediately
1480  if (debug)
1481  cerr << "fatal_semantic_parse_exception: ";
1482  cerr << pe.what() <<nl;
1483  return;
1484  } catch ( syntactic_parse_exception& pe ) { // wrong content after = sign etc..
1485  // syntactic errors must be reported immediately
1486  if (debug)
1487  cerr << "syntactic_parse_exception: ";
1488  cerr << pe.what() <<nl;
1489  return;
1490  } catch ( parse_exception_parser_fail &pe )
1491  {
1492  // ignore, try next parser
1493  if (debug) {
1494  cerr << "Ignoring ValueStatement exception :"<<nl;
1495  cerr << pe.what() <<nl;
1496  }
1497  } catch ( parse_exception& pe ) {
1498  // syntactic errors must be reported immediately
1499  if (debug)
1500  cerr << "parse_exception :";
1501  cerr << pe.what() <<nl;
1502  return;
1503  }
1504  if (debug)
1505  cerr << "Trying Expression..."<<nl;
1506  try {
1507  // Check if it was a method or datasource :
1508  last_expr = _parser.parseExpression( comm, context );
1509  // methods and DS'es are processed immediately.
1510  if ( last_expr ) {
1511  // only print if no ';' was given.
1512  assert( comm.size() != 0 );
1513  if ( comm[ comm.size() - 1 ] != ';' ) {
1514  this->printResult( last_expr.get(), true );
1515  cout << sresult.str() << nl << endl;
1516  sresult.str("");
1517  } else
1518  last_expr->evaluate();
1519  return; // done here
1520  } else if (debug)
1521  cerr << "returned (null) !"<<nl;
1522  } catch ( syntactic_parse_exception& pe ) { // missing brace etc
1523  // syntactic errors must be reported immediately
1524  if (debug)
1525  cerr << "syntactic_parse_exception :";
1526  cerr << pe.what() <<nl;
1527  return;
1528  } catch ( fatal_semantic_parse_exception& pe ) { // incorr args, ...
1529  // way to fatal, must be reported immediately
1530  if (debug)
1531  cerr << "fatal_semantic_parse_exception :";
1532  cerr << pe.what() <<nl;
1533  return;
1534  } catch ( parse_exception_parser_fail &pe )
1535  {
1536  // We're the last parser!
1537  if (debug)
1538  cerr << "Ignoring Expression exception :"<<nl;
1539  cerr << pe.what() <<nl;
1540 
1541  } catch ( parse_exception& pe ) {
1542  // We're the last parser!
1543  if (debug)
1544  cerr << "Ignoring Expression parse_exception :"<<nl;
1545  cerr << pe.what() <<nl;
1546  }
1547  }
1548 
1549  void TaskBrowser::printResult( base::DataSourceBase* ds, bool recurse) {
1550  std::string prompt(" = ");
1551  // setup prompt :
1552  sresult <<prompt<< setw(20)<<left;
1553  if ( ds )
1554  doPrint( ds, recurse );
1555  else
1556  sresult << "(null)";
1557  sresult << right;
1558  }
1559 
1560  void TaskBrowser::doPrint( base::DataSourceBase::shared_ptr ds, bool recurse) {
1561  if (!ds) {
1562  sresult << "(null)";
1563  return;
1564  }
1565 
1566  // this is needed for ds's that rely on initialision.
1567  // e.g. eval true once or time measurements.
1568  // becomes only really handy for 'watches' (to deprecate).
1569  ds->reset();
1570  // this is needed to read a ds's value. Otherwise, a cached value may be returned.
1571  ds->evaluate();
1572 
1573  DataSource<RTT::PropertyBag>* dspbag = DataSource<RTT::PropertyBag>::narrow(ds.get());
1574  if (dspbag) {
1575  RTT::PropertyBag bag( dspbag->get() );
1576  if (!recurse) {
1577  int siz = bag.getProperties().size();
1578  int wdth = siz ? (20 - (siz / 10 + 1)) : 20;
1579  sresult <<setw(0)<< siz <<setw( wdth )<< " Properties";
1580  } else {
1581  if ( ! bag.empty() ) {
1582  sresult <<setw(0)<<nl;
1583  for( RTT::PropertyBag::iterator it= bag.getProperties().begin(); it!=bag.getProperties().end(); ++it) {
1584  sresult <<setw(14)<<right<< Types()->toDot( (*it)->getType() )<<" "<<coloron<<setw(14)<< (*it)->getName()<<coloroff;
1585  base::DataSourceBase::shared_ptr propds = (*it)->getDataSource();
1586  this->printResult( propds.get(), false );
1587  sresult <<" ("<<(*it)->getDescription()<<')' << nl;
1588  }
1589  } else {
1590  sresult <<prompt<<"(empty RTT::PropertyBag)";
1591  }
1592  }
1593  return;
1594  }
1595 
1596  // Print the members of the type:
1597  base::DataSourceBase::shared_ptr dsb(ds);
1598  if (dsb->getMemberNames().empty() || dsb->getTypeInfo()->isStreamable() ) {
1599  if (debug) cerr << "terminal item " << dsb->getTypeName() << nl;
1600  if (usehex)
1601  sresult << std::hex << dsb;
1602  else
1603  sresult << std::dec << dsb;
1604  } else {
1605  sresult << setw(0);
1606  sresult << "{";
1607  vector<string> names = dsb->getMemberNames();
1608  if ( find(names.begin(), names.end(), "capacity") != names.end() &&
1609  find(names.begin(), names.end(), "size") != names.end() ) {
1610  // is a container/sequence:
1611  DataSource<int>::shared_ptr seq_size = dynamic_pointer_cast<DataSource<int> >(dsb->getMember("size"));
1612  if (seq_size) {
1613  ValueDataSource<unsigned int>::shared_ptr index = new ValueDataSource<unsigned int>(0);
1614  // print max 10 items of sequence:
1615  sresult << " [";
1616  for (int i=0; i != seq_size->get(); ++i) {
1617  index->set( i );
1618  if (i == 10) {
1619  sresult << "...("<< seq_size->get() - 10 <<" items omitted)...";
1620  break;
1621  } else {
1622  DataSourceBase::shared_ptr element = dsb->getMember(index, DataSourceBase::shared_ptr() );
1623  doPrint(element, true);
1624  if (i+1 != seq_size->get())
1625  sresult <<", ";
1626  }
1627  }
1628  sresult << " ], "; // size and capacity will follow...
1629  }
1630  }
1631  for(vector<string>::iterator it = names.begin(); it != names.end(); ) {
1632  sresult << *it << " = ";
1633  doPrint( dsb->getMember(*it), true);
1634  if (++it != names.end())
1635  sresult <<", ";
1636  }
1637  sresult <<" }";
1638  }
1639  }
1640 
1641  struct comcol
1642  {
1643  const char* command;
1644  comcol(const char* c) :command(c) {}
1645  std::ostream& operator()( std::ostream& os ) const {
1646  os<<"'"<< TaskBrowser::coloron<< TaskBrowser::underline << command << TaskBrowser::coloroff<<"'";
1647  return os;
1648  }
1649  };
1650 
1651  struct keycol
1652  {
1653  const char* command;
1654  keycol(const char* c) :command(c) {}
1655  std::ostream& operator()( std::ostream& os )const {
1656  os<<"<"<< TaskBrowser::coloron<< TaskBrowser::underline << command << TaskBrowser::coloroff<<">";
1657  return os;
1658  }
1659  };
1660 
1661  struct titlecol
1662  {
1663  const char* command;
1664  titlecol(const char* c) :command(c) {}
1665  std::ostream& operator()( std::ostream& os ) const {
1666  os<<endl<<"["<< TaskBrowser::coloron<< TaskBrowser::underline << command << TaskBrowser::coloroff<<"]";
1667  return os;
1668  }
1669  };
1670 
1671  std::ostream& operator<<(std::ostream& os, comcol f ){
1672  return f(os);
1673  }
1674 
1675  std::ostream& operator<<(std::ostream& os, keycol f ){
1676  return f(os);
1677  }
1678 
1679  std::ostream& operator<<(std::ostream& os, titlecol f ){
1680  return f(os);
1681  }
1682 
1684  {
1685  cout << coloroff;
1686  cout <<titlecol("Task Browsing")<<nl;
1687  cout << " To switch to another task, type "<<comcol("cd <path-to-taskname>")<<nl;
1688  cout << " and type "<<comcol("cd ..")<<" to go back to the previous task (History size is 20)."<<nl;
1689  cout << " Pressing "<<keycol("tab")<<" multiple times helps you to complete your command."<<nl;
1690  cout << " It is not mandatory to switch to a task to interact with it, you can type the"<<nl;
1691  cout << " peer-path to the task (dot-separated) and then type command or expression :"<<nl;
1692  cout << " PeerTask.OtherTask.FinalTask.countTo(3) [enter] "<<nl;
1693  cout << " Where 'countTo' is a method of 'FinalTask'."<<nl;
1694  cout << " The TaskBrowser starts by default 'In' the current component. In order to watch"<<nl;
1695  cout << " the TaskBrowser itself, type "<<comcol("leave")<<" You will notice that it"<<nl;
1696  cout << " has connected to the data ports of the visited component. Use "<<comcol("enter")<<" to enter"<<nl;
1697  cout << " the visited component again. The "<<comcol("cd")<<" command works transparantly in both"<<nl;
1698  cout << " modi."<<nl;
1699 
1700  cout << " "<<titlecol("Task Context Info")<<nl;
1701  cout << " To see the contents of a task, type "<<comcol("ls")<<nl;
1702  cout << " For a detailed argument list (and helpful info) of the object's methods, "<<nl;
1703  cout <<" type the name of one of the listed task objects : " <<nl;
1704  cout <<" this [enter]" <<nl<<nl;
1705  cout <<" factor( int number ) : bool" <<nl;
1706  cout <<" Factor a value into its primes." <<nl;
1707  cout <<" number : The number to factor in primes." <<nl;
1708  cout <<" isRunning( ) : bool" <<nl;
1709  cout <<" Is this RTT::TaskContext started ?" <<nl;
1710  cout <<" loadProgram( const& std::string Filename ) : bool" <<nl;
1711  cout <<" Load an Orocos Program Script from a file." <<nl;
1712  cout <<" Filename : An ops file." <<nl;
1713  cout <<" ..."<<nl;
1714 
1715  cout << " A status character shows the TaskState of a component."<<nl;
1716  cout << " 'E':RunTimeError, 'S':Stopped, 'R':Running, 'U':PreOperational (Unconfigured)"<<nl;
1717  cout << " 'X':Exception, 'F':FatalError" << nl;
1718 
1719  cout <<titlecol("Expressions")<<nl;
1720  cout << " You can evaluate any script expression by merely typing it :"<<nl;
1721  cout << " 1+1 [enter]" <<nl;
1722  cout << " = 2" <<nl;
1723  cout << " or inspect the status of a program :"<<nl;
1724  cout << " myProgram.isRunning [enter]" <<nl;
1725  cout << " = false" <<nl;
1726  cout << " and display the contents of complex data types (vector, array,...) :"<<nl;
1727  cout << " array(6)" <<nl;
1728  cout << " = {0, 0, 0, 0, 0, 0}" <<nl;
1729 
1730  cout <<titlecol("Changing Attributes and Properties")<<nl;
1731  cout << " To change the value of a Task's attribute, type "<<comcol("varname = <newvalue>")<<nl;
1732  cout << " If you provided a correct assignment, the browser will inform you of the success"<<nl;
1733  cout <<" with the set value." <<nl;
1734 
1735  cout <<titlecol("Operations")<<nl;
1736  cout << " An Operation is sent or called (evaluated) "<<nl;
1737  cout << " immediately and print the result. An example could be :"<<nl;
1738  cout << " someTask.bar.getNumberOfBeers(\"Palm\") [enter] "<<nl;
1739  cout << " = 99" <<nl;
1740  cout << " You can ask help on an operation by using the 'help' command: "<<nl;
1741  cout << " help start"<<nl;
1742  cout << " start( ) : bool"<<nl;
1743  cout << " Start this TaskContext (= startHook() + updateHook() )." <<nl;
1744 
1745  cout <<titlecol("Program and scripting::StateMachine Scripts")<<nl;
1746  cout << " To load a program script use the scripting service."<<nl;
1747  cout << " Use "<<comcol(".provide scripting")<< " to load the scripting service in a TaskContext."<<nl;
1748  cout << " You can use "<<comcol("ls progname")<<nl;
1749  cout << " to see the programs operations and variables. You can manipulate each one of these"<<nl;
1750  cout << " using the service object of the program."<<nl;
1751 
1752  cout << " To print a program or state machine listing, use "<<comcol("list progname [linenumber]")<<nl;
1753  cout << " to list the contents of the current program lines being executed,"<<nl;
1754  cout << " or 10 lines before or after <linenumber>. When only "<<comcol("list [n]")<<nl;
1755  cout << " is typed, 20 lines of the last listed program are printed from line <n> on "<<nl;
1756  cout << " ( default : list next 20 lines after previous list )."<<nl;
1757 
1758  cout << " To trace a program or state machine listing, use "<<comcol("trace [progname]")<<" this will"<<nl;
1759  cout << " cause the TaskBrowser to list the contents of a traced program,"<<nl;
1760  cout << " each time the line number of the traced program changes."<<nl;
1761  cout << " Disable tracing with "<<comcol("untrace [progname]")<<""<<nl;
1762  cout << " If no arguments are given to "<<comcol("trace")<<" and "<<comcol("untrace")<<", it applies to all programs."<<nl;
1763 
1764  cout << " A status character shows which line is being executed."<<nl;
1765  cout << " For programs : 'E':Error, 'S':Stopped, 'R':Running, 'P':Paused"<<nl;
1766  cout << " For state machines : <the same as programs> + 'A':Active, 'I':Inactive"<<nl;
1767 
1768  cout <<titlecol("Changing Colors")<<nl;
1769  cout << " You can inform the TaskBrowser of your background color by typing "<<comcol(".dark")<<nl;
1770  cout << " "<<comcol(".light")<<", or "<<comcol(".nocolors")<<" to increase readability."<<nl;
1771 
1772  cout <<titlecol("Output Formatting")<<nl;
1773  cout << " Use the commands "<<comcol(".hex") << " or " << comcol(".nohex") << " to turn hexadecimal "<<nl;
1774  cout << " notation of integers on or off."<<nl;
1775 
1776  cout <<titlecol("Macro Recording / RTT::Command line history")<<nl;
1777  cout << " You can browse the commandline history by using the up-arrow key or press "<<comcol("Ctrl r")<<nl;
1778  cout << " and a search term. Hit enter to execute the current searched command."<<nl;
1779  cout << " Macros can be recorded using the "<<comcol(".record 'macro-name'")<<" command."<<nl;
1780  cout << " You can cancel the recording by typing "<<comcol(".cancel")<<" ."<<nl;
1781  cout << " You can save and load the macro by typing "<<comcol(".end")<<" . The macro becomes"<<nl;
1782  cout << " available as a command with name 'macro-name' in the current TaskContext." << nl;
1783  cout << " While you enter the macro, it is not executed, as you must use scripting syntax which"<<nl;
1784  cout << " may use loop or conditional statements, variables etc."<<nl;
1785 
1786  cout <<titlecol("Connecting Ports")<<nl;
1787  cout << " You can instruct the TaskBrowser to connect to the ports of the current Peer by"<<nl;
1788  cout << " typing "<<comcol(".connect [port-name]")<<", which will temporarily create connections"<<nl;
1789  cout << " to all ports if [port-name] is omitted or to the specified port otherwise."<<nl;
1790  cout << " The TaskBrowser disconnects these ports when it visits another component, but the"<<nl;
1791  cout << " created connection objects remain in place (this is more or less a bug)!"<<nl;
1792 
1793  cout <<titlecol("Plugins, Typekits and Services")<<nl;
1794  cout << " Use "<<comcol(".provide [servicename]")<< " to load a service in a TaskContext."<<nl;
1795  cout << " For example, to add XML marshalling, type: "<<comcol(".provide marshalling")<< "."<<nl;
1796  cout << " Use "<<comcol(".services")<< " to get a list of available services."<<nl;
1797  cout << " Use "<<comcol(".typekits")<< " to get a list of available typekits."<<nl;
1798  cout << " Use "<<comcol(".types")<< " to get a list of available data types."<<nl;
1799  }
1800 
1801  void TaskBrowser::printHelp( string helpstring ) {
1802  peer = context;
1803  // trim garbage:
1804  str_trim(helpstring, ' ');
1805  str_trim(helpstring, '.');
1806 
1807  if ( printService(helpstring))
1808  return;
1809 
1810  if ( findPeer( helpstring ) ) {
1811  try {
1812  // findPeer resolved the taskobject holding 'helpstring'.
1813  sresult << nl;
1814  if (helpstring.rfind('.') != string::npos )
1815  printOperation( helpstring.substr(helpstring.rfind('.')+1 ), taskobject );
1816  else
1817  printOperation( helpstring, taskobject );
1818  cout << sresult.str();
1819  } catch (...) {
1820  cerr<< " help: No such operation known: '"<< helpstring << "'"<<nl;
1821  }
1822  } else {
1823  cerr<< " help: No such operation known (peer not found): '"<< helpstring << "'"<<nl;
1824  }
1825  sresult.str("");
1826  }
1827 
1828  void TaskBrowser::printProgram(const std::string& progname, int cl /*= -1*/, RTT::TaskContext* progpeer /* = 0 */) {
1829  string ps;
1830  char s;
1831  stringstream txtss;
1832  int ln;
1833  int start;
1834  int end;
1835  bool found(false);
1836 
1837  if (progpeer == 0 )
1838  progpeer = context;
1839 
1840  // if program exists, display.
1841  if ( progpeer->getProvider<Scripting>("scripting")->hasProgram( progname ) ) {
1842  s = getProgramStatusChar(progpeer, progname);
1843  txtss.str( progpeer->getProvider<Scripting>("scripting")->getProgramText(progname) );
1844  ln = progpeer->getProvider<Scripting>("scripting")->getProgramLine(progname);
1845  if ( cl < 0 ) cl = ln;
1846  start = cl < 10 ? 1 : cl - 10;
1847  end = cl + 10;
1848  this->listText( txtss, start, end, ln, s);
1849  found = true;
1850  }
1851 
1852  // If statemachine exists, display.
1853  if ( progpeer->getProvider<Scripting>("scripting")->hasStateMachine( progname ) ) {
1854  s = getStateMachineStatusChar(progpeer, progname);
1855  txtss.str( progpeer->getProvider<Scripting>("scripting")->getStateMachineText(progname) );
1856  ln = progpeer->getProvider<Scripting>("scripting")->getStateMachineLine(progname);
1857  if ( cl < 0 ) cl = ln;
1858  start = cl <= 10 ? 1 : cl - 10;
1859  end = cl + 10;
1860  this->listText( txtss, start, end, ln, s);
1861  found = true;
1862  }
1863  if ( !found ) {
1864  cerr << "Error : No such program or state machine found : "<<progname;
1865  cerr << " in "<< progpeer->getName() <<"."<<endl;
1866  return;
1867  }
1868  storedname = progname;
1869  }
1870 
1871  void TaskBrowser::printProgram(int cl /* = -1 */) {
1872  string ps;
1873  char s;
1874  stringstream txtss;
1875  int ln;
1876  int start;
1877  int end;
1878  bool found(false);
1879  if ( context->getProvider<Scripting>("scripting")->hasProgram( storedname ) ) {
1880  s = getProgramStatusChar(context, storedname);
1881  txtss.str( context->getProvider<Scripting>("scripting")->getProgramText(storedname) );
1882  ln = context->getProvider<Scripting>("scripting")->getProgramLine(storedname);
1883  if ( cl < 0 ) cl = storedline;
1884  if (storedline < 0 ) cl = ln -10;
1885  start = cl;
1886  end = cl + 20;
1887  this->listText( txtss, start, end, ln, s);
1888  found = true;
1889  }
1890  if ( context->getProvider<Scripting>("scripting")->hasStateMachine(storedname) ) {
1891  s = getStateMachineStatusChar(context, storedname);
1892  txtss.str( context->getProvider<Scripting>("scripting")->getStateMachineText(storedname) );
1893  ln = context->getProvider<Scripting>("scripting")->getStateMachineLine(storedname);
1894  if ( cl < 0 ) cl = storedline;
1895  if (storedline < 0 ) cl = ln -10;
1896  start = cl;
1897  end = cl+20;
1898  this->listText( txtss, start, end, ln, s);
1899  found = true;
1900  }
1901  if ( !found )
1902  cerr << "Error : No such program or state machine found : "<<storedname<<endl;
1903  }
1904 
1905  void TaskBrowser::listText(stringstream& txtss,int start, int end, int ln, char s) {
1906  int curln = 1;
1907  string line;
1908  while ( start > 1 && curln != start ) { // consume lines
1909  getline( txtss, line, '\n' );
1910  if ( ! txtss )
1911  break; // no more lines, break.
1912  ++curln;
1913  }
1914  while ( end > start && curln != end ) { // print lines
1915  getline( txtss, line, '\n' );
1916  if ( ! txtss )
1917  break; // no more lines, break.
1918  if ( curln == ln ) {
1919  cout << s<<'>';
1920  }
1921  else
1922  cout << " ";
1923  cout<< setw(int(log(double(end)))) <<right << curln<< left;
1924  cout << ' ' << line <<endl;
1925  ++curln;
1926  }
1927  storedline = curln;
1928  // done !
1929  }
1930 
1931  void TaskBrowser::printInfo(const std::string& peerp)
1932  {
1933  // this sets this->peer to the peer given
1934  peer = context;
1935  taskobject = peer->provides();
1936  if ( !peerp.empty() && peerp != "." && this->findPeer( peerp+"." ) == 0 ) {
1937  cerr << "No such peer or object: " << peerp << endl;
1938  return;
1939  }
1940 
1941  if ( !peer || !peer->ready()) {
1942  cout << nl << " Connection to peer "+peerp+" lost (peer->ready() == false)." <<endlog();
1943  return;
1944  }
1945 
1946  // sresult << *it << "["<<getTaskStatusChar(peer->getPeer(*it))<<"] ";
1947 
1948 
1949  if ( peer->provides() == taskobject )
1950  sresult <<nl<<" Listing TaskContext "<< green << peer->getName()<<coloroff << "["<<getTaskStatusChar(peer)<<"] :"<<nl;
1951  else
1952  sresult <<nl<<" Listing Service "<< green << taskobject->getName()<<coloroff<< "["<<getTaskStatusChar(peer)<<"] :"<<nl;
1953 
1954  sresult <<nl<<" Configuration Properties: ";
1955  RTT::PropertyBag* bag = taskobject->properties();
1956  if ( bag && bag->size() != 0 ) {
1957  // Print Properties:
1958  for( RTT::PropertyBag::iterator it = bag->begin(); it != bag->end(); ++it) {
1959  base::DataSourceBase::shared_ptr pds = (*it)->getDataSource();
1960  sresult << nl << setw(11)<< right << Types()->toDot( (*it)->getType() )<< " "
1961  << coloron <<setw(14)<<left<< (*it)->getName() << coloroff;
1962  this->printResult( pds.get(), false ); // do not recurse
1963  sresult<<" ("<< (*it)->getDescription() <<')';
1964  }
1965  } else {
1966  sresult << "(none)";
1967  }
1968  sresult <<nl;
1969 
1970  // Print "this" interface (without detail) and then list objects...
1971  sresult <<nl<< " Provided Interface:";
1972 
1973  sresult <<nl<< " Attributes : ";
1974  std::vector<std::string> objlist = taskobject->getAttributeNames();
1975  if ( !objlist.empty() ) {
1976  sresult << nl;
1977  // Print Attributes:
1978  for( std::vector<std::string>::iterator it = objlist.begin(); it != objlist.end(); ++it) {
1979  base::DataSourceBase::shared_ptr pds = taskobject->getValue(*it)->getDataSource();
1980  sresult << setw(11)<< right << Types()->toDot( pds->getType() )<< " "
1981  << coloron <<setw( 14 )<<left<< *it << coloroff;
1982  this->printResult( pds.get(), false ); // do not recurse
1983  sresult <<nl;
1984  }
1985  } else {
1986  sresult << coloron << "(none)";
1987  }
1988 
1989  sresult <<coloroff<<nl<< " Operations : "<<coloron;
1990  objlist = taskobject->getNames();
1991  if ( !objlist.empty() ) {
1992  std::copy(objlist.begin(), objlist.end(), std::ostream_iterator<std::string>(sresult, " "));
1993  } else {
1994  sresult << "(none)";
1995  }
1996  sresult << coloroff << nl;
1997 
1998  sresult <<nl<< " Data Flow Ports: ";
1999  objlist = taskobject->getPortNames();
2000  if ( !objlist.empty() ) {
2001  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it) {
2002  base::PortInterface* port = taskobject->getPort(*it);
2003  bool writer = dynamic_cast<OutputPortInterface*>(port) ? true : false;
2004  // Port type R/W
2005  sresult << nl << " " << ( !writer ?
2006  " In" : "Out");
2007  // Port data type + name
2008  if ( !port->connected() )
2009  sresult << "(U) " << setw(11)<<right<< Types()->toDot( port->getTypeInfo()->getTypeName() );
2010  else
2011  sresult << "(C) " << setw(11)<<right<< Types()->toDot( port->getTypeInfo()->getTypeName() );
2012  sresult << " "
2013  << coloron <<setw( 14 )<<left<< *it << coloroff;
2014 
2015  InputPortInterface* iport = dynamic_cast<InputPortInterface*>(port);
2016  if (iport) {
2017  sresult << " <= ( use '"<< iport->getName() << ".read(sample)' to read a sample from this port)";
2018  }
2019  OutputPortInterface* oport = dynamic_cast<OutputPortInterface*>(port);
2020  if (oport) {
2021  if ( oport->keepsLastWrittenValue()) {
2022  DataSourceBase::shared_ptr dsb = oport->getDataSource();
2023  dsb->evaluate(); // read last written value.
2024  sresult << " => " << dsb;
2025  } else
2026  sresult << " => (keepsLastWrittenValue() == false. Enable it for this port in order to see it in the TaskBrowser.)";
2027  }
2028 #if 0
2029  // only show if we're connected to it
2030  if (peer == taskcontext && peer->provides() == taskobject) {
2031  // Lookup if we have an input with that name and
2032  // consume the last sample this port produced.
2033  InputPortInterface* iport = dynamic_cast<InputPortInterface*>(ports()->getPort(port->getName()));
2034  if (iport) {
2035  // consume sample
2036  iport->getDataSource()->evaluate();
2037  // display
2038  if ( peer == this)
2039  sresult << " <= " << DataSourceBase::shared_ptr( iport->getDataSource());
2040  else
2041  sresult << " => " << DataSourceBase::shared_ptr( iport->getDataSource());
2042  }
2043  OutputPortInterface* oport = dynamic_cast<OutputPortInterface*>(ports()->getPort(port->getName()));
2044  if (oport) {
2045  // display last written value:
2046  DataSourceBase::shared_ptr ds = oport->getDataSource();
2047  if (ds) {
2048  if ( peer == this)
2049  sresult << " => " << ds;
2050  else
2051  sresult << " <= " << ds << " (sent from TaskBrowser)";
2052  } else {
2053  sresult << "(no last written value kept)";
2054  }
2055  }
2056  } else {
2057  sresult << "(TaskBrowser not connected to this port)";
2058  }
2059 #endif
2060  // Port description (see Service)
2061 // if ( peer->provides(*it) )
2062 // sresult << " ( "<< taskobject->provides(*it)->getDescription() << " ) ";
2063  }
2064  } else {
2065  sresult << "(none)";
2066  }
2067  sresult << coloroff << nl;
2068 
2069  objlist = taskobject->getProviderNames();
2070  sresult <<nl<< " Services: "<<nl;
2071  if ( !objlist.empty() ) {
2072  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it)
2073  sresult <<coloron<< " " << setw(14) << *it <<coloroff<< " ( "<< taskobject->provides(*it)->doc() << " ) "<<nl;
2074  } else {
2075  sresult <<coloron<< "(none)" <<coloroff <<nl;
2076  }
2077 
2078  // RTT::TaskContext specific:
2079  if ( peer->provides() == taskobject ) {
2080 
2081  objlist = peer->requires()->getOperationCallerNames();
2082  sresult <<nl<< " Requires Operations :";
2083  if ( !objlist.empty() ) {
2084  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it)
2085  sresult <<coloron<< " " << *it <<coloroff << '[' << (peer->requires()->getOperationCaller(*it)->ready() ? "R]" : "!]");
2086  sresult << nl;
2087  } else {
2088  sresult <<coloron<< " (none)" <<coloroff <<nl;
2089  }
2090  objlist = peer->requires()->getRequesterNames();
2091  sresult << " Requests Services :";
2092  if ( !objlist.empty() ) {
2093  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it)
2094  sresult <<coloron<< " " << *it <<coloroff << '[' << (peer->requires(*it)->ready() ? "R]" : "!]");
2095  sresult << nl;
2096  } else {
2097  sresult <<coloron<< " (none)" <<coloroff <<nl;
2098  }
2099 
2100  if (peer->provides()->hasService("scripting")) {
2101  objlist = peer->getProvider<Scripting>("scripting")->getProgramList();
2102  if ( !objlist.empty() ) {
2103  sresult << " Programs : "<<coloron;
2104  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it)
2105  sresult << *it << "["<<getProgramStatusChar(peer,*it)<<"] ";
2106  sresult << coloroff << nl;
2107  }
2108 
2109  objlist = peer->getProvider<Scripting>("scripting")->getStateMachineList();
2110  if ( !objlist.empty() ) {
2111  sresult << " StateMachines: "<<coloron;
2112  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it)
2113  sresult << *it << "["<<getStateMachineStatusChar(peer,*it)<<"] ";
2114  sresult << coloroff << nl;
2115  }
2116  }
2117 
2118  // if we are in the TB, display the peers of our connected task:
2119  if ( context == tb )
2120  sresult <<nl<< " "<<peer->getName()<<" Peers : "<<coloron;
2121  else
2122  sresult << nl <<" Peers : "<<coloron;
2123 
2124  objlist = peer->getPeerList();
2125  if ( !objlist.empty() )
2126  for(vector<string>::iterator it = objlist.begin(); it != objlist.end(); ++it) {
2127  if( peer->getPeer(*it) )
2128  sresult << *it << "["<<getTaskStatusChar(peer->getPeer(*it))<<"] ";
2129  else
2130  sresult << *it << "[X] ";
2131  }
2132  else
2133  sresult << "(none)";
2134  }
2135  sresult <<coloroff<<nl;
2136  cout << sresult.str();
2137  sresult.str("");
2138  }
2139 
2140  void TaskBrowser::printOperation( const std::string m, Service::shared_ptr the_ops )
2141  {
2142  std::vector<ArgumentDescription> args;
2143  Service::shared_ptr ops;
2144  try {
2145  args = the_ops->getArgumentList( m ); // may throw !
2146  ops = the_ops;
2147  } catch(...) {
2148  args = GlobalService::Instance()->getArgumentList( m ); // may throw !
2149  ops = GlobalService::Instance();
2150  }
2151  sresult <<" " << coloron << m << coloroff<< "( ";
2152  for (std::vector<ArgumentDescription>::iterator it = args.begin(); it != args.end(); ++it) {
2153  sresult << Types()->toDot( it->type ) <<" ";
2154  sresult << coloron << it->name << coloroff;
2155  if ( it+1 != args.end() )
2156  sresult << ", ";
2157  else
2158  sresult << " ";
2159  }
2160  sresult << ") : "<< Types()->toDot( ops->getResultType(m) )<<nl;
2161  sresult << " " << ops->getDescription( m )<<nl;
2162  for (std::vector<ArgumentDescription>::iterator it = args.begin(); it != args.end(); ++it)
2163  sresult <<" "<< it->name <<" : " << it->description << nl;
2164  }
2165 
2166 }
static std::string coloron
The &#39;turn color on&#39; escape sequence.
static std::string underline
The &#39;underline&#39; escape sequence.
Use colors suitable for a white background.
static std::string green
The green color.
void evaluate(std::string &comm)
Execute/evaluate a string which may be a command, method, expression etc.
void printResult(RTT::base::DataSourceBase *ds, bool recurse)
Evaluate a internal::DataSource and print the result.
void evalCommand(std::string &comm)
Evaluate command.
void browserAction(std::string &act)
Execute a specific browser action, such as "loadProgram pname", "loadStateMachine smname"...
STL namespace.
char getTaskStatusChar(RTT::TaskContext *t)
Helper functions to display task and script states.
void printProgram(const std::string &pn, int line=-1, RTT::TaskContext *progpeer=0)
Print a program listing of a loaded program centered at line line.
static std::string blue
The blue color.
The Orocos Component Library.
Definition: Component.hpp:43
bool printService(const std::string name)
Print the synopsis of a Service.
void printHelp()
Print the help page.
void switchTaskContext(std::string &path)
Switch to a peer RTT::TaskContext using a path .
Do not use colors.
TaskBrowser(RTT::TaskContext *c)
Create a TaskBrowser which initially visits a given RTT::TaskContext c.
void loop()
Call this method from ORO_main() to process keyboard input and thus startup the TaskBrowser.
void printInfo(const std::string &peerpath)
Print info this peer or another peer at "peerpath".
Definition: Category.hpp:10
ColorTheme
The kinds of color themes the TaskBrowser supports.
static std::string prompt
The prompt.
static std::string coloroff
The &#39;turn color off&#39; escape sequence.
Use colors suitable for a dark background.
void switchBack()
Go to the previous peer in the visit history.
void printOperation(const std::string m, Service::shared_ptr ops)
Print the synopsis of an Operation.
static std::string red
The red color.