Orocos Real-Time Toolkit  2.8.3
StateMachine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  tag: Peter Soetens Mon May 10 19:10:29 CEST 2004 StateMachine.cxx
3 
4  StateMachine.cxx - description
5  -------------------
6  begin : Mon May 10 2004
7  copyright : (C) 2004 Peter Soetens
8  email : peter.soetens@mech.kuleuven.ac.be
9 
10  ***************************************************************************
11  * This library is free software; you can redistribute it and/or *
12  * modify it under the terms of the GNU General Public *
13  * License as published by the Free Software Foundation; *
14  * version 2 of the License. *
15  * *
16  * As a special exception, you may use this file as part of a free *
17  * software library without restriction. Specifically, if other files *
18  * instantiate templates or use macros or inline functions from this *
19  * file, or you compile this file and link it with other files to *
20  * produce an executable, this file does not by itself cause the *
21  * resulting executable to be covered by the GNU General Public *
22  * License. This exception does not however invalidate any other *
23  * reasons why the executable file might be covered by the GNU General *
24  * Public License. *
25  * *
26  * This library is distributed in the hope that it will be useful, *
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
29  * Lesser General Public License for more details. *
30  * *
31  * You should have received a copy of the GNU General Public *
32  * License along with this library; if not, write to the Free Software *
33  * Foundation, Inc., 59 Temple Place, *
34  * Suite 330, Boston, MA 02111-1307 USA *
35  * *
36  ***************************************************************************/
37 #include "StateMachine.hpp"
38 #include "../ExecutionEngine.hpp"
39 #include "../internal/DataSource.hpp"
40 #include "../Service.hpp"
41 #include "CommandFunctors.hpp"
42 #include <Logger.hpp>
43 #include <functional>
44 
45 #include <assert.h>
46 #include <boost/bind.hpp>
47 #include <boost/tuple/tuple.hpp>
48 #include "internal/mystd.hpp"
49 
50 #define TRACE(msg) do { \
51  if (!mtrace) \
52  break; \
53  Logger::In in( _name ); \
54  log(Info) << '[' << this->getStatusStr() << ']' << std::string(" ") + msg <<endlog(); \
55 } while(0)
56 
57 namespace RTT {
58  using namespace detail;
59  using boost::tuples::get;
60  using namespace std;
61  using namespace boost;
62 
63 
64  std::string StateMachine::emptyString;
65 
66  StateMachine::StateMachine(StateMachinePtr parent, const string& name )
67  : smpStatus(nill), _parent (parent) , _name(name), smStatus(Status::unloaded),
68  initstate(0), finistate(0), current( 0 ), next(0), initc(0),
69  currentProg(0), currentExit(0), currentHandle(0), currentEntry(0), currentRun(0), currentTrans(0),
70  checking_precond(false), mstep(false), mtrace(false), evaluating(0)
71  {
72  this->addState(0); // allows global state transitions
73  }
74 
76  {
77  if ( this->isLoaded() ){
78  getEngine()->removeFunction(this);
79  }
80  delete initc;
81  TRACE( "StateMachine '" + _name + "' destroyed." );
82  }
83 
85  TRACE( "Being Loaded in ExecutionEngine." );
87  for(TransitionMap::iterator it=stateMap.begin(); it != stateMap.end(); ++it) {
88  // inform all entry/exit state scripts:
89  if (it->first)
90  it->first->loaded( this->getEngine() );
91  // inform all transition scripts
92  for(TransList::iterator tlit= it->second.begin(); tlit != it->second.end(); ++tlit ) {
93  if ( get<4>(*tlit) )
94  get<4>(*tlit)->loaded( this->getEngine() );
95  }
96  }
97  for( EventMap::iterator it = eventMap.begin(); it != eventMap.end(); ++it)
98  {
99  // inform all transition scripts
100  for(EventList::iterator tlit= it->second.begin(); tlit != it->second.end(); ++tlit ) {
101  if ( get<5>(*tlit) )
102  get<5>(*tlit)->loaded( this->getEngine() );
103  }
104  }
105  }
106 
108  TRACE( "Being unloaded from ExecutionEngine." );
109  if ( this->isActive() == false)
110  return;
111  if ( this->isReactive() )
112  this->requestFinalState();
113  if ( this->isAutomatic() )
114  this->stop();
115  if (this->currentState() != this->getFinalState() )
116  this->execute(); // try one last time
117  if (this->currentState() != this->getFinalState() )
118  log(Critical) << "Failed to bring StateMachine "<< this->getName()
119  << " into the final state. Program stalled in state '"
120  << this->currentState()->getName()<<"' line number "
121  << this->getLineNumber()<<endlog(); // critical failure !
122  }
123 
125  return smStatus;
126  }
127 
128  string StateMachine::getStatusStr() const {
129 
130  switch ( smStatus )
131  {
132  case Status::inactive:
133  return "inactive";
134  break;
135  case Status::stopping:
136  return "stopping";
137  break;
138  case Status::stopped:
139  return "stopped";
140  break;
141  case Status::requesting:
142  return "requesting";
143  break;
144  case Status::running:
145  return "running";
146  break;
147  case Status::paused:
148  return "paused";
149  break;
150  case Status::active:
151  return "active";
152  break;
153  case Status::activating:
154  return "activating";
155  break;
157  return "deactivating";
158  break;
159  case Status::resetting:
160  return "resetting";
161  break;
162  case Status::error:
163  return "error";
164  break;
165  case Status::unloaded:
166  return "unloaded";
167  break;
168  }
169  return "na";
170  }
171 
173  {
175  TRACE( "Will pause." );
176  if (currentProg) {
177  currentProg->pause();
178  currentProg->execute();
179  }
180  smpStatus = pausing;
181  return true;
182  }
183  TRACE( "Won't pause." );
184  return false;
185  }
186 
188  {
189  if ( smStatus == Status::paused && mstep == false ) {
190  TRACE( "Will step." );
191  mstep = true;
192  return true;
193  }
194  if ( smStatus == Status::active ) {
195  TRACE( "Will step." );
197  return true;
198  }
199  TRACE( "Won't step." );
200  return false;
201  }
202 
204  {
205  return this->automatic();
206  }
207 
209  {
210  // if you go from reactive to automatic,
211  // first execute the run program, before
212  // evaluating transitions.
214  TRACE( "Will start." );
216  os::MutexLock lock(execlock);
217  runState( current );
218  return true;
219  }
220  TRACE( "Won't start." );
221  return false;
222  }
223 
225  {
227  TRACE( "Will enter reactive mode." );
229  return true;
230  }
231  TRACE( "Won't enter reactive mode." );
232  return false;
233  }
234 
236  {
238  TRACE( "Will stop." );
239  smpStatus = gostop;
240  return true;
241  }
242  TRACE( "Won't stop." );
243  return false;
244  }
245 
247  {
248  // if waiting in final state, go ahead.
249  if ( smStatus == Status::stopped ) {
250  TRACE( "Will reset.");
251  smpStatus = goreset;
252  return true;
253  }
254  TRACE("Won't reset.");
255  return false;
256  }
257 
259  {
260  os::MutexLock lock(execlock);
261  // before dealing with transitional states,
262  // check if we're actually running.
264  smpStatus = nill;
265  return true;
266  }
267 
268  // internal transitional issues.
269  switch (smpStatus) {
270  case pausing:
271  TRACE("Is paused now.");
273  smpStatus = nill;
274  return true;
275  break;
276  case gostop:
277  this->executePending();
278  if ( this->requestFinalState() ) {
279  // test for atomicity :
280  if ( this->inTransition() ) {
282  } else {
283  TRACE("Is stopped now.");
285  }
286  smpStatus = nill;
287  }
288  return true;
289  break;
290  case goreset:
291  if ( this->executePending() ) {
292  this->requestInitialState();
293  if ( this->inTransition() ) {
295  } else {
296  TRACE("Is reset now.");
298  }
299  }
300  smpStatus = nill;
301  return true;
302  break;
303  case nill:
304  break;
305  }
306 
307  // public visible states.
308  switch (smStatus) {
309  case Status::inactive:
310  return true;
311  break;
312  case Status::requesting:
313  if ( this->executePending() ) { // if all steps done,
314  this->requestNextState();
315  this->executePending(); // execute steps of next state
316  TRACE("Is active now.");
318  }
319  break;
320  case Status::active:
321  this->executePending();
322  break;
323  case Status::running:
324  if ( this->executePending() == false)
325  break;
326  // if all pending done:
327  this->requestNextState(); // one state at a time
328  this->executePending(); // execute steps of next state
329  break;
330  case Status::paused:
331  if (mstep) {
332  if ( this->executePending(true) ) { // if all steps done,
333  this->requestNextState(true); // one state at a time
334  this->executePending(true); // execute steps of next state
335  }
336  TRACE("Did a step.");
337  mstep = false;
338  }
339  break;
340  case Status::error:
341  case Status::unloaded:
342  break;
343  case Status::activating:
344  this->executePending();
345  if ( !this->inTransition() ) {
346  TRACE("Is active now.");
348  }
349  break;
350  case Status::stopping:
351  if ( this->executePending() ) {
352  TRACE("Is stopped now.");
354  }break;
356  if ( this->executePending() ) {
357  TRACE("Is inactive now.");
359  }break;
360  case Status::resetting:
361  if ( this->executePending() ) {
362  TRACE("Is reset now.");
364  }break;
365  case Status::stopped:
366  this->executePending();
367  if ( current != finistate ) {// detect leaving final state by event/request
369  } break;
370  }
371  return true;
372  }
373 
375  {
376  os::MutexLock lock(execlock);
377  // all conditions that must be satisfied to enter the initial state :
378  if ( interruptible() && ( current == initstate || current == finistate ) )
379  {
380  TRACE("Will enter initial state.");
381  // this will try to execute the state change atomically
382  this->requestStateChange( initstate );
383  return true;
384  }
385  TRACE("Won't enter initial state.");
386  return false;
387  }
388 
390  {
391  os::MutexLock lock(execlock);
392  // if we are inactive or in transition, don't do anything.
393  if ( current == 0 || ( !inError() && !interruptible() ) ) {
394  TRACE("Won't enter final state.");
395  return false;
396  }
397 
398  // this will try to execute the state change atomically
399  if ( this->requestStateChange( finistate ) ) {
400  TRACE("Will enter final state.");
401  return true;
402  }
403  TRACE("Won't enter final state.");
404  return false;
405  }
406 
407  void StateMachine::changeState(StateInterface* newState, ProgramInterface* transProg, bool stepping) {
408  if ( newState == current )
409  {
410  // this is only true if current state was selected in a transition of current.
411  if ( transProg ) {
412  TRACE("Transition triggered to self: '"+current->getName()+"'");
413  transProg->reset();
414  if (transProg->start() == false )
416  currentTrans = transProg;
417  // manually reset reqstep, or the next iteration would skip transition checks.
418  reqstep = stateMap.find( current )->second.begin();
419  // from now on, we are in transition to self !
420  // currentRun is _not_ set to zero or reset.
421  // it is/may be interrupted by trans, then continued.
422  // if already in handle, it is completed before trans, then trans is executed.
423  // since a transProg was given, no handle is executed.
424  } else {
425  // execute the default action (schedule handle )
426  // if no transition to another state took place and no transprog specified.
427  // only schedule a handle if not yet in progress.
428  if ( currentHandle == 0 )
429  handleState( current );
430  }
431  }
432  else
433  {
434  TRACE("Transition triggered from '"+ (current ? current->getName() : "null") +"' to '"+(newState ? newState->getName() : "null")+"'.");
435  // reset handle and run, in case it is still set ( during error
436  // or when an event arrived ).
437  currentRun = 0;
438  currentHandle = 0;
439  if ( transProg ) {
440  transProg->reset();
441  if ( transProg->start() == false )
443 
444  }
445  next = newState;
446  currentTrans = transProg;
447  // if error in current Exit, skip it.
448  if ( currentExit && currentExit->inError() )
449  currentExit = 0;
450  leaveState(current);
451  }
452  // schedule a run for the next 'step'.
453  // if handle above finished, run will be called directly
454  // in executePending. if handle was not finished
455  // or stepping, it will be called after handle.
456  runState( newState );
457  }
458 
460  {
461  enableEvents(0);
462  }
464  {
465  disableEvents(0);
466  }
468  {
469 // if (s) {
470 // TRACE("Enabling events for state '"+s->getName()+"'.");
471 // } else
472 // TRACE("Enabling global events.");
473  EventMap::mapped_type& hlist = eventMap[s];
474  for (EventList::iterator eit = hlist.begin();
475  eit != hlist.end();
476  ++eit) {
477  assert( get<6>(*eit).connected() == false );
478  get<6>(*eit).connect();
479  }
480  }
482  {
483 // if (s) {
484 // TRACE("Disabling events for state '"+s->getName()+"'.");
485 // } else
486 // TRACE("Disabling global events.");
487  EventMap::mapped_type& hlist = eventMap[s];
488  for (EventList::iterator eit = hlist.begin();
489  eit != hlist.end();
490  ++eit) {
491  assert( get<6>(*eit).connected() == true );
492  get<6>(*eit).disconnect();
493  }
494 
495  }
496 
498  const std::string& ename, vector<DataSourceBase::shared_ptr> args,
499  StateInterface* from, StateInterface* to,
500  ConditionInterface* guard, boost::shared_ptr<ProgramInterface> transprog,
501  StateInterface* elseto, boost::shared_ptr<ProgramInterface> elseprog )
502  {
503  Logger::In in("StateMachine::createEventTransition");
504  DisposableInterface::shared_ptr di = sp->getLocalOperation(ename);
505  OperationCallerInterface::shared_ptr oci = dynamic_pointer_cast<OperationCallerInterface>(di);
506  if ( !oci ) {
507  log(Error) << "Can not receive event '"<< ename <<"' in StateMachine : not a local operation."<< endlog();
508  return false;
509  }
510 
511  if ( !( sp && guard ) ) {
512  log(Error) << "Invalid arguments for event '"<< ename <<"'. ";
513  if (!sp)
514  log() <<"Service was null. ";
515  if (!guard)
516  log() <<"Guard Condition was null. ";
517  log() << endlog();
518  return false;
519  }
520 
521  if ( to == 0 )
522  to = from;
523 
524  // get ename from event service, provide args as arguments
525  // event should be activated upon entry of 'from'
526  // guard is evaluated to get final 'ok'.
527  // event does a requestStateChange( to );
528  // if 'ok', execute transprog during transition.
529 
530  // Store guard and transprog for copy/clone semantics.
531  // upon SM copy, recreate the handles for the copy SM with a copy of guard/transprog.
532  // Same for args. I guess we need to store all arguments of this function to allow
533  // proper copy semantics, such that the event handle can be created for each new SM
534  // instance. Ownership of guard and transprog is to be determined, but seems to ly
535  // with the SM. handle.destroy() can be called upon SM destruction.
536  Handle handle;
537 
538  log(Debug) << "Creating Signal handler for Operation '"<< ename <<"' from state "<< (from ? from->getName() : string("(global)")) << " to state " << ( to ? to->getName() : string("(global)") ) <<Logger::endl;
539 #ifdef ORO_SIGNALLING_OPERATIONS
540  // don't deliver this signal asynchronously, we want it right away.
541  handle = sp->produceSignal( ename, new CommandFunction( boost::bind( &StateMachine::eventTransition, this, from, guard, transprog.get(), to, elseprog.get(), elseto) ), args, 0 );
542 #endif
543  if ( !handle.ready() ) {
544  Logger::log() << Logger::Error << "Could not setup handle for event '"<<ename<<"'."<<Logger::endl;
545  return false; // event does not exist...
546  }
547  // all our handles must start in disconnected state:
548  handle.disconnect();
549  // BIG NOTE : we MUST store handle otherwise, the connection is destroyed (cfr setup vs connect).
550  // Off course, we also need it to connect/disconnect the event.
551  eventMap[from].push_back( boost::make_tuple( sp, ename, args, to, guard, transprog, handle, elseto, elseprog) );
552  // add the states to the statemap.
553  stateMap[from];
554  stateMap[to];
555  return true;
556  }
557 
558  bool StateMachine::eventTransition(StateInterface* from, ConditionInterface* c, ProgramInterface* p, StateInterface* to, ProgramInterface* elsep, StateInterface* elseto )
559  {
560  // called by event to begin Transition to 'to'.
561  // This interrupts the current run program at yield point
562  // the transition and/or exit program can cleanup...
563 
564  MutexLock lock(execlock); // recursive
565 
566  if ( !current)
567  return true;
568 
569  // Only transition if this event was meant for this state and we are not
570  // in transition already.
571  // If condition fails, check precondition 'else' state (if present) and
572  // execute else program (may be null).
573 
574  if (from == 0)
575  from = current;
576  if (to == 0)
577  to = current;
578  TRACE("Received Signal in state '"+ current->getName()+"' for transition from state '" + from->getName() + "' to state '" + to->getName() + "'");
579  if ( from == current && !this->inTransition() ) {
580  if ( c->evaluate() && checkConditions(to, false) == 1 ) {
581  TRACE( "Valid transition from " + from->getName() +
582  +" to "+to->getName()+".");
583  // stepping == true ! We don't want to execute the whole state transition
584  changeState( to, p, true ); // valid transition to 'to'.
585  // trigger EE in order to force execution of the remainder of the state transition:
586  this->getEngine()->getActivity()->trigger();
587  }
588  else {
589  TRACE( "Rejected transition from " + from->getName() +
590  " to " + to->getName() +
591  " within state " + current->getName() + ": guards failed.");
592  }
593  }
594 #if 1
595  else {
596  if (this->inTransition() ) {
597  TRACE( "Rejected transition from " + from->getName() +
598  " within " + current->getName() + ": already in transition.");
599  } else {
600  TRACE( "Rejected transition from " + from->getName() +
601  + " within " + current->getName() + ": wrong state.");
602  }
603  }
604 #endif
605  return true;
606  }
607 
609  {
610  os::MutexLock lock(execlock);
611  // bad idea, user, don't run this if we're not active...
612  if( current == 0 )
613  return 0;
614  // only a run program may be interrupted...
615  if ( !interruptible() || currentTrans ) {
616  return current; // can not accept request, still in transition.
617  }
618 
619  // Reset global conditions.
620  TransList::const_iterator it, it1, it2;
621  it1 = stateMap.find( 0 )->second.begin();
622  it2 = stateMap.find( 0 )->second.end();
623 
624  if ( reqstep == stateMap.find( current )->second.begin() ) // avoid reseting too much in stepping mode.
625  for ( it= it1; it != it2; ++it)
626  get<0>(*it)->reset();
627 
628  if ( reqstep == reqend ) { // if nothing to evaluate, eval globals, then just handle()
629 
630  for ( ; it1 != it2; ++it1 )
631  if ( get<0>(*it1)->evaluate()
632  && checkConditions( get<1>(*it1) ) == 1 ) {
633  StateInterface* next = get<1>(*it1);
634  if ( next == 0 ) // handle current if no next
635  changeState( current, get<4>(*it1).get(), stepping );
636  else
637  changeState( next, get<4>(*it1).get(), stepping );
638  // the request was accepted
639  return current;
640  }
641  // no transition found: handle()
642  changeState( current, 0, stepping );
643  return current;
644  }
645 
646  // if we got here, at least one evaluation to check
647  do {
648  if ( get<0>(*reqstep)->evaluate() ) {
649  // evaluate() might call stop() or other sm functions:
650  if (reqstep == reqend )
651  return current;
652  // check preconds of target state :
653  int cres = checkConditions( get<1>(*reqstep), stepping );
654  if (cres == 0) {
655  break; // only returned in stepping
656  }
657  if( cres == 1) {
658  changeState( get<1>(*reqstep), get<4>(*reqstep).get(), stepping );
659  break; // valid transition
660  }
661  // if cres == -1 : precondition failed, increment reqstep...
662  }
663  if ( reqstep + 1 == reqend ) {
664  // to a state specified by the user (global)
665  for ( ; it1 != it2; ++it1 ) {
666  if ( get<0>(*it1)->evaluate() && checkConditions( get<1>(*it1) ) == 1 ) {
667  StateInterface* next = get<1>(*it1);
668  if ( next == 0) // handle current if no next
669  changeState( current, get<4>(*it1).get(), stepping );
670  else
671  changeState( next, get<4>(*it1).get(), stepping );
672  // the request was accepted
673  return current;
674  }
675  }
676  // no transition was found, reset and 'schedule' a handle :
677  reqstep = stateMap.find( current )->second.begin();
678  evaluating = get<3>(*reqstep);
679  changeState( current, 0, stepping );
680  break;
681  }
682  else {
683  ++reqstep;
684  evaluating = get<3>(*reqstep);
685  }
686  } while ( !stepping );
687 
688  return current;
689  }
690 
691  int StateMachine::checkConditions( StateInterface* state, bool stepping ) {
692 
693  // if the preconditions of \a state are checked the first time in stepping mode, reset the iterators.
694  if ( !checking_precond || !stepping ) {
695  prec_it = precondMap.equal_range(state); // state is the _target_ state
696  }
697 
698  // will be set to true if stepping below.
699  //checking_precond = false;
700 
701  while ( prec_it.first != prec_it.second ) {
702  if (checking_precond == false && stepping ) {
703  evaluating = prec_it.first->second.second; // indicate we will evaluate this line (if any).
704  checking_precond = true;
705  return 0;
706  }
707  if ( prec_it.first->second.first->evaluate() == false ) {
708  checking_precond = false;
709  return -1; // precondition failed
710  }
711  ++( prec_it.first );
712  if (stepping) {
713  if ( prec_it.first != prec_it.second )
714  evaluating = prec_it.first->second.second; // indicate we will evaluate the next line (if any).
715  checking_precond = true;
716  return 0; // not done yet.
717  }
718  }
719  checking_precond = false;
720  return 1; // success !
721  }
722 
723 
725  {
726  os::MutexLock lock(execlock);
727  // bad idea, user, don't run this if we're not active...
728  if ( current == 0 )
729  return 0;
730  TransList::const_iterator it1, it2;
731  it1 = stateMap.find( current )->second.begin();
732  it2 = stateMap.find( current )->second.end();
733 
734  for ( ; it1 != it2; ++it1 )
735  if ( get<0>(*it1)->evaluate() && checkConditions( get<1>(*it1)) == 1 ) {
736  return get<1>(*it1);
737  }
738 
739  // also check the global transitions.
740  it1 = stateMap.find( 0 )->second.begin();
741  it2 = stateMap.find( 0 )->second.end();
742 
743  for ( ; it1 != it2; ++it1 )
744  if ( get<0>(*it1)->evaluate() && checkConditions( get<1>(*it1)) == 1 ) {
745  return get<1>(*it1);
746  }
747 
748  return current;
749  }
750 
751  std::vector<std::string> StateMachine::getStateList() const {
752  vector<string> result;
753  vector<StateInterface*> sl;
754  StateInterface* dummy = 0;
755  transform( stateMap.begin(), stateMap.end(), back_inserter(sl), select1st<TransitionMap::value_type>() );
756  sl.erase( find(sl.begin(), sl.end(), dummy) );
757  transform( sl.begin(), sl.end(), back_inserter(result), boost::bind( &StateInterface::getName, _1 ) );
758  return result;
759  }
760 
762  {
763  stateMap[s];
764  }
765 
766 
767  StateInterface* StateMachine::getState(const string& name) const
768  {
769  TransitionMap::const_iterator it = stateMap.begin();
770  while ( it != stateMap.end() ) {
771  if ( it->first && it->first->getName() == name )
772  return it->first;
773  ++it;
774  }
775  return 0;
776  }
777 
779  {
780  os::MutexLock lock(execlock);
781  // bad idea, user, don't run this if we're not active...
782  if( current == 0 )
783  return false;
784 
785  if ( !interruptible() ) {
786  return false; // can not accept request, still in transition
787  }
788 
789  // we may make transition to next state :
790 
791  // to current state
792  if ( current == s_n )
793  {
794  changeState( s_n, 0 );
795  executePending();
796  return true;
797  }
798 
799  // between 2 states specified by the user.
800  TransList::iterator it, it1, it2;
801  it1 = stateMap.find( current )->second.begin();
802  it2 = stateMap.find( current )->second.end();
803 
804  for ( ; it1 != it2; ++it1 )
805  if ( get<1>(*it1) == s_n
806  && get<0>(*it1)->evaluate()
807  && checkConditions( s_n ) == 1 ) {
808  changeState( s_n, get<4>(*it1).get() );
809  // the request was accepted
810  executePending();
811  return true;
812  }
813 
814  // to a state specified by the user (global)
815  it1 = stateMap.find( 0 )->second.begin();
816  it2 = stateMap.find( 0 )->second.end();
817 
818  // reset all conditions
819  for ( it= it1; it != it2; ++it)
820  get<0>(*it)->reset();
821 
822  // evaluate them
823  for ( ; it1 != it2; ++it1 )
824  if ( get<1>(*it1) == s_n
825  && get<0>(*it1)->evaluate()
826  && checkConditions( s_n ) == 1 ) {
827  changeState( s_n, get<4>(*it1).get() );
828  // the request was accepted
829  executePending();
830  return true;
831  }
832 
833  // to final state
834  if ( finistate == s_n )
835  {
836  changeState( s_n, 0 );
837  executePending();
838  return true;
839  }
840 
841  // to inital state from final state
842  if ( initstate == s_n && current == finistate)
843  {
844  changeState( s_n, 0 );
845  executePending();
846  return true;
847  }
848 
849  // the request has failed.
850  return false;
851  }
852 
854  // if not activated, return first line.
855  // work around race condition that current[Prog] may become zero,
856  // thus first store in local variable :
857  StateInterface* statecopy = current;
858  if ( statecopy == 0 )
859  return 1;
860  ProgramInterface* copy = currentProg;
861  if ( copy )
862  return copy->getLineNumber();
863  if ( evaluating )
864  return evaluating;
865 
866  // if none of the above, return entry point :
867  return statecopy->getEntryPoint();
868  }
869 
870  string StateMachine::getText() const {
871  return string();
872  }
873 
875  {
876  // we must be inactive.
877  if ( current != 0)
878  return;
879  precondMap.insert( make_pair(state, make_pair( cnd, line)) );
880  stateMap[state]; // add to state map.
881  }
882 
883  void StateMachine::transitionSet( StateInterface* from, StateInterface* to, ConditionInterface* cnd, int priority, int line )
884  {
885  this->transitionSet( from, to, cnd, boost::shared_ptr<ProgramInterface>(), priority, line);
886  }
887 
889  ConditionInterface* cnd, boost::shared_ptr<ProgramInterface> transprog,
890  int priority, int line )
891  {
892  // we must be inactive.
893  if ( current != 0)
894  return;
895 
896  if (from) {
897  TRACE("Created transition from "+from->getName() +"' to '"+ to->getName()+"'");
898  } else {
899  TRACE("Created global transition to '"+ to->getName()+"'");
900  }
901  // insert both from and to in the statemap
902  TransList::iterator it;
903  for ( it= stateMap[from].begin(); it != stateMap[from].end() && get<2>(*it) >= priority; ++it)
904  ; // this ';' is intentional
905  stateMap[from].insert(it, boost::make_tuple( cnd, to, priority, line, transprog ) );
906  stateMap[to]; // insert empty vector for 'to' state.
907  }
908 
910  {
911  return current;
912  }
913 
915  {
916  return currentProg;
917  }
918 
920  {
921  assert(s);
922 // TRACE( "Planning to leave state " + s->getName() );
923  currentExit = s->getExitProgram();
924  if ( currentExit ) {
925  currentExit->reset();
926  if (currentExit->start() == false)
928  }
929  }
930 
932  {
933  assert(s);
934  currentRun = s->getRunProgram();
935  if ( currentRun ) {
936  currentRun->reset();
937  if (currentRun->start() == false)
939  }
940  }
941 
943  {
944  assert(s);
945  currentHandle = s->getHandleProgram();
946  if ( currentHandle ) {
947  currentHandle->reset();
948  if (currentHandle->start() == false)
950  }
951  }
952 
954  {
955  assert(s);
956 // TRACE( "Planning to enter state " + s->getName() );
957 
958  // Before a state is entered, all transitions are reset !
959  TransList::iterator it;
960  for ( it= stateMap.find(s)->second.begin(); it != stateMap.find(s)->second.end(); ++it)
961  get<0>(*it)->reset();
962 
963  currentEntry = s->getEntryProgram();
964  if ( currentEntry ) {
965  currentEntry->reset();
966  if (currentEntry->start() == false)
968  }
969  }
970 
971  bool StateMachine::executePending( bool stepping )
972  {
973  // This function has great resposibility, since it acts like
974  // a scheduler for pending requests. It tries to devise what to
975  // do on basis of the contents of variables (like current*, next,...).
976  // This is a somewhat
977  // fragile implementation but requires very little bookkeeping.
978  // if returns true : a transition (exit/entry) is done
979  // and a new state may be requested.
980 
981  if ( inError() )
982  return false;
983 
984  TRACE("executePending..." );
985 
986  if ( currentEntry ) {
987  TRACE("Executing entry program of '"+ (current ? current->getName() : "(null)") +"'" );
988  if ( this->executeProgram(currentEntry, stepping) == false )
989  return false;
990  // done.
991  TRACE("Finished entry program of '"+ (current ? current->getName() : "(null)") +"'" );
992  // in stepping mode, delay 'true' one executePending().
993  if ( stepping ) {
994  currentProg = currentRun;
995  return false;
996  }
997  }
998  // from this point on, events must be enabled.
999 
1000  // first try to execute transition program on behalf of current state.
1001  if ( currentTrans ) {
1002  TRACE("Executing transition program from '"+ (current ? current->getName() : "(null)") + "' to '"+ ( next ? next->getName() : "(null)")+"'" );
1003  // exception : transition during handle, first finish handle !
1004  if ( currentHandle ) {
1005  if ( this->executeProgram(currentHandle, stepping) == false )
1006  return false;
1007  } else
1008  if ( this->executeProgram(currentTrans, stepping) == false )
1009  return false;
1010  // done.
1011  TRACE("Finished transition program from '"+ (current ? current->getName() : "(null)") + "' to '"+ ( next ? next->getName() : "(null)")+"'" );
1012  // in stepping mode, delay 'true' one executePending().
1013  if ( stepping ) {
1014  currentProg = currentExit ? currentExit : (currentEntry ? currentEntry : currentRun);
1015  return false;
1016  }
1017  }
1018 
1019  // last is exit
1020  if ( currentExit ) {
1021  TRACE("Executing exit program from '"+ (current ? current->getName() : "(null)") + "' (going to '"+ (next ? next->getName() : "(null)") +"')" );
1022  if ( this->executeProgram(currentExit, stepping) == false )
1023  return false;
1024  // done.
1025  TRACE("Finished exit program from '"+ (current ? current->getName() : "(null)") + "' (going to '"+ (next ? next->getName() : "(null)") +"')" );
1026  // in stepping mode, delay 'true' one executePending().
1027  if ( stepping ) {
1028  currentProg = (currentEntry ? currentEntry : currentRun);
1029  return false;
1030  }
1031  }
1032 
1033  // we only get here if all entry/transition/exit programs have been executed.
1034  // so now we schedule a new entry program for the next state.
1035 
1036  // only reset the reqstep if we changed state.
1037  // if we did not change state, it will be reset in requestNextState().
1038  if ( current != next ) {
1039  if ( next ) {
1040  reqstep = stateMap.find( next )->second.begin();
1041  reqend = stateMap.find( next )->second.end();
1042  // init for getLineNumber() :
1043  if ( reqstep == reqend )
1044  evaluating = 0;
1045  else
1046  evaluating = get<3>(*reqstep);
1047  } else {
1048  current = 0;
1049  return true; // done if current == 0 !
1050  }
1051  // make change transition after exit of previous state:
1052  TRACE("Formally transitioning from '"+ (current ? current->getName() : "(null)") + "' to '"+ (next ? next->getName() : "(null)") +"'" );
1053 
1054  disableEvents(current);
1055  current = next;
1056  enableEvents(current);
1057  enterState(current);
1058 
1059  }
1060 
1061  // give new current a chance to execute the entry program and run program :
1062  if ( currentEntry ) {
1063  TRACE("Executing entry program of '"+ (current ? current->getName() : "(null)") +"'" );
1064  if ( this->executeProgram(currentEntry, stepping) == false )
1065  return false;
1066  // done.
1067  TRACE("Finished entry program of '"+ (current ? current->getName() : "(null)") +"'" );
1068  // in stepping mode, delay 'true' one executePending().
1069  if ( stepping ) {
1070  currentProg = currentRun;
1071  return false;
1072  }
1073  }
1074 
1075  // Handle is executed after the transitions failed.
1076  if ( currentHandle ) {
1077  TRACE("Executing handle program of '"+ (current ? current->getName() : "(null)") +"'" );
1078  if ( this->executeProgram(currentHandle, stepping) == false )
1079  return false;
1080  // done.
1081  TRACE("Finished handle program of '"+ (current ? current->getName() : "(null)") +"'" );
1082  // in stepping mode, delay 'true' one executePending().
1083  if ( stepping ) {
1084  currentProg = currentRun;
1085  return false;
1086  }
1087  }
1088 
1089  // Run is executed before the transitions.
1090  if ( currentRun ) {
1091  TRACE("Executing run program of '"+ (current ? current->getName() : "(null)") +"'" );
1092  if ( this->executeProgram(currentRun, stepping) == false )
1093  return false;
1094  // done.
1095  TRACE("Finished run program of '"+ (current ? current->getName() : "(null)") +"'" );
1096  // in stepping mode, delay 'true' one executePending().
1097  if ( stepping )
1098  return false;
1099  }
1100 
1101  return true; // all pending is done
1102  }
1103 
1105  {
1106  if ( cp == 0)
1107  return false;
1108  // execute this stateprogram and cleanup if needed.
1109  currentProg = cp;
1110  if ( stepping )
1111  currentProg->step();
1112  else
1113  currentProg->execute();
1114  if ( currentProg && currentProg->inError() ) {
1116  smpStatus = nill;
1117  TRACE("Encountered run-time error at line " << this->getLineNumber() );
1118  return false;
1119  }
1120 
1121  if ( currentProg && !currentProg->isStopped() )
1122  return false;
1123 
1124  cp = currentProg = 0;
1125  return true;
1126  }
1127 
1128 
1129  // in exit/entry or transition programs
1131  return ( (currentProg != 0) && (currentProg != currentRun) && (currentProg != currentHandle) ) || (current != next);
1132  }
1133 
1134  // only run program may be interrupted.
1136  return currentProg == 0 || currentProg == currentRun;
1137  }
1138 
1140  {
1141  initstate = s;
1142  stateMap[initstate];
1143  }
1144 
1146  {
1147  finistate = s;
1148  stateMap[finistate];
1149  }
1150 
1151  void StateMachine::trace(bool t) {
1152  mtrace =t;
1153  }
1154 
1156  {
1157  // inactive implies loaded, but check additionally if smp is at least active
1158  if ( smStatus != Status::inactive ) {
1159  TRACE("Won't activate: already active.");
1160  return false;
1161  }
1162  os::MutexLock lock(execlock);
1163 
1164  smpStatus = nill;
1165 
1166  if ( this->checkConditions( getInitialState() ) != 1 ) {
1167  TRACE("Won't activate: preconditions failed.");
1168  return false; //preconditions not met.
1169  }
1170 
1171  if ( initc ) {
1172  initc->reset();
1173  initc->readArguments();
1174  if ( initc->execute() == false ) {
1175  TRACE("Won't activate: Init Commands failed.");
1176  return false; // fail to activate.
1177  }
1178  }
1179 
1180  current = getInitialState();
1181  next = getInitialState();
1183  reqstep = stateMap.find( next )->second.begin();
1184  reqend = stateMap.find( next )->second.end();
1185 
1186  // Enable all event handlers
1188  enableEvents(current);
1189 
1190  // execute the entry program of the initial state.
1191  if ( !inError() ) {
1192  if ( this->executePending() ) {
1194  TRACE("Activated.");
1195  } else {
1196  if ( !inError() ) {
1197  TRACE("Still activating.");
1199  }
1200  }
1201  }
1202 
1203  return true;
1204  }
1205 
1206 
1208  {
1209  // the only time to refuse executing this is when we did set ourselves to inactive before.
1210  if ( smStatus == Status::inactive) {
1211  TRACE("Won't deactivate: already inactive.");
1212  return false;
1213  }
1214  os::MutexLock lock(execlock);
1215 
1216  // disable all event handlers
1218 
1219  // whatever state we are in, leave it.
1220  // but if current exit is in error, skip it alltogether.
1221  if ( currentExit && currentExit->inError() )
1222  currentExit = 0;
1223  if ( currentTrans && currentTrans->inError() )
1224  currentTrans = 0;
1225  // if we stalled, in previous deactivate
1226  // even skip/stop exit program.
1227  if ( next != 0 && current ) {
1228  leaveState( current );
1229  disableEvents(current);
1230  } else {
1231  currentExit = 0;
1232  currentTrans = 0;
1233  }
1234 
1235  // do not call enterState( 0 )
1236  currentProg = 0;
1237  currentEntry = 0;
1238  currentHandle = 0;
1239  currentRun = 0;
1240  next = 0;
1241 
1242  // this will execute the exitFunction (if any) and, if successfull,
1243  // set current to zero (using next).
1244  if ( this->executePending() ) {
1245  TRACE("Deactivated.");
1247  } else {
1248  TRACE("Still deactivating.");
1250  }
1251 
1252  return true;
1253  }
1254 }
ActivityInterface * getActivity() const
Query for the task this interface is run in.
bool executePending(bool stepping=false)
Execute any pending State (exit, entry, handle) programs.
virtual bool trigger()=0
Trigger that work has to be done.
void loading()
Informs this object that it got loaded in an ExecutionEngine.
void transitionSet(StateInterface *from, StateInterface *to, ConditionInterface *cnd, int priority, int line)
Express a possible transition from one state to another under a certain condition.
The interface class for operation callers.
virtual bool removeFunction(base::ExecutableInterface *f)
Remove a running function added with runFunction.
bool isAutomatic() const
Query if the state machine is reacting to events and evaluating transition conditions.
virtual void reset()
Reset this action.
bool isStopped() const
Returns true if the program is not executing (stopped) or not loaded.
std::vector< std::string > getStateList() const
Get a list of the names of all the present states.
void enableEvents(StateInterface *s)
bool isLoaded()
Returns true if this object is loaded in an ExecutionEngine.
virtual void reset()=0
Reset the execution point to the beginning of this program interface.
This interface represents the concept of a condition which can be evaluated and return true or false...
bool deactivate()
Stop this StateMachine.
bool interruptible() const
Inspect if the StateMachine is interruptible by events.
virtual ~StateMachine()
The destructor is virtual since ParsedStateMachine still inherits this class.
const std::string & getName() const
This method must be overloaded to get a useful hierarchy.
STL namespace.
bool reset()
Reset the state machine from the final state to the initial state and wait for events or requests...
virtual void readArguments()=0
This is invoked some time before execute() at a time when the action may read its function arguments...
virtual int getEntryPoint() const =0
Get the beginning definition of this State.
virtual bool pause()=0
Pause or start-and-pause the execution of this program.
std::string getStatusStr() const
Get the status in a readable string format.
virtual const std::string & getName() const =0
Get the name of this state.
void setFinalState(StateInterface *s)
Set the final state of this StateMachine.
void disableEvents(StateInterface *s)
void preconditionSet(StateInterface *state, ConditionInterface *cnd, int line)
Express a precondition for entering a state.
virtual ProgramInterface * getEntryProgram() const =0
Get the entry program of this State.
boost::shared_ptr< OperationCallerInterface > shared_ptr
Use this type for shared pointer storage of an OperationCallerInterface object.
bool pause()
Pause the state machine.
An execution engine serialises (executes one after the other) the execution of all commands...
ProgramInterface * currentProgram() const
Retrieve the current program in execution.
virtual ProgramInterface * getHandleProgram() const =0
Get the handle program of this State.
#define TRACE(msg)
StateInterface * nextState()
Search from the current state a candidate next state.
bool execute()
Used by the StateMachineProcessor to execute the next action(s) or state transitions.
int getLineNumber() const
Returns the current program line in execution,.
virtual bool start()=0
Start the execution of this program.
bool step()
Execute a single action if the state machine is paused or evaluate the transition conditions if the s...
virtual bool step()=0
Execute a single action when paused.
void handleState(StateInterface *s)
virtual bool execute()=0
Execute as much actions until the program needs to wait on a condition to become true.
bool inError() const
Get the error status of this StateMachine.
Status::StateMachineStatus getStatus() const
Get the status of this state machine.
boost::shared_ptr< Service > ServicePtr
Definition: rtt-fwd.hpp:86
void addState(StateInterface *s)
Add a State.
virtual bool execute()=0
Execute the functionality of this action.
virtual int getLineNumber() const =0
Return the current &#39;line number&#39; of the program.
StateInterface * currentState() const
Retrieve the current state of the state machine.
Status::StateMachineStatus smStatus
static std::ostream & endl(std::ostream &__os)
Definition: Logger.cpp:383
void unloading()
Informs this object that it got unloaded from an ExecutionEngine.
bool executeProgram(ProgramInterface *&cp, bool stepping)
void trace(bool on_off)
Turn log(Debug) messages on or off to track state transitions.
boost::shared_ptr< StateMachine > StateMachinePtr
void changeState(StateInterface *s, ProgramInterface *tprog, bool stepping=false)
bool start()
Enter automatic mode: evaluating the transition conditions continuously.
boost::shared_ptr< DisposableInterface > shared_ptr
Use this type for shared pointer storage of an DisposableInterface object.
StateInterface * getInitialState() const
Retrieve the initial state of the state machine.
ExecutionEngine * getEngine()
Returns the ExecutionEngine this object is loaded into or null otherwise.
Enumerates all possible state machine statuses.
PreConditionMap precondMap
A map keeping track of all preconditions of a state.
virtual ProgramInterface * getExitProgram() const =0
Get the exit program of this State.
void runState(StateInterface *s)
bool requestInitialState()
Request going to the Initial State.
virtual ProgramInterface * getRunProgram() const =0
Get the run program of this State.
bool activate()
Start this StateMachine.
StateInterface * getState(const std::string &name) const
Lookup a State by name.
bool requestFinalState()
Request going to the Final State.
Notify the Logger in which &#39;module&#39; the message occured.
Definition: Logger.hpp:159
virtual bool evaluate()=0
Evaluate the Condition and return the outcome.
static Logger & log()
As Instance(), but more userfriendly.
Definition: Logger.cpp:117
bool isReactive() const
Query if the state machine is currently reacting only to events.
StateInterface * requestNextState(bool stepping=false)
Search from the current state a candidate next state.
void setInitialState(StateInterface *s)
Set the initial state of this StateMachine.
int checkConditions(StateInterface *state, bool stepping=false)
Contains TaskContext, Activity, OperationCaller, Operation, Property, InputPort, OutputPort, Attribute.
Definition: Activity.cpp:51
TransitionMap stateMap
A map keeping track of all States and conditional transitions between two states. ...
A functor with the base::ActionInterface, for the case where the functor is a bool(void).
A State contains an entry, run, handle and exit program.
A Program represents a collection of instructions that can be stepwise executed.
bool requestStateChange(StateInterface *s_n)
Request a state transition to a new state.
StateInterface * getFinalState() const
Retrieve the final state of the state machine.
bool isActive() const
Returns true if the state machine is activated.
The Handle holds the information, and allows manipulation, of a connection between a internal::Signal...
Definition: Handle.hpp:66
bool reactive()
Switch to reactive mode from automatic mode.
bool inTransition() const
Inspect if the StateMachine is performing a state transition.
bool inError() const
Returns true if the program is in error.
StateMachine(StateMachinePtr parent, const std::string &name="Default")
Create a new StateMachine with an optional parent.
bool automatic()
Enter automatic mode: evaluating the transition conditions continuously.
EventMap eventMap
A map keeping track of all events of a state.
bool stop()
Bring the state machine to the safe final state and wait for events or requests.
void enterState(StateInterface *s)
void leaveState(StateInterface *s)
MutexLock is a scope based Monitor, protecting critical sections with a Mutex object through locking ...
Definition: MutexLock.hpp:51
bool createEventTransition(ServicePtr sp, ExecutionEngine *target_engine, const std::string &ename, std::vector< base::DataSourceBase::shared_ptr > args, StateInterface *from, StateInterface *to, ConditionInterface *guard, boost::shared_ptr< ProgramInterface > transprog, StateInterface *elseto=0, boost::shared_ptr< ProgramInterface > elseprog=boost::shared_ptr< ProgramInterface >())
Express a possible transition from one state to another when an Event is fired under a certain condit...
virtual std::string getText() const
Return the text to which getLineNumber() refers.