Skip to content

Commit

Permalink
scripting: make state transition execution more logical
Browse files Browse the repository at this point in the history
Upon each step, first the run state is executed and then transitions are checked.
If a transition is valid, it is executed upto the entry state of the next state.
Executing run is then a task for the next step again.

Signed-off-by: Peter Soetens <peter@intermodalics.eu>

Conflicts:
	rtt/scripting/StateMachine.cpp
  • Loading branch information
Peter Soetens authored and meyerj committed Feb 3, 2016
1 parent c005354 commit bec07dd
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 58 deletions.
116 changes: 71 additions & 45 deletions rtt/scripting/StateMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,13 @@ namespace RTT {
bool StateMachine::automatic()
{
TRACE_INIT();
// if you go from reactive to automatic,
// first execute the run program, before
// evaluating transitions.
//
if ( smStatus != Status::inactive && smStatus != Status::unloaded && smStatus != Status::error) {
TRACE( "Will start." );
smStatus = Status::running;
os::MutexLock lock(execlock);
runState( current );

return true;
}
TRACE( "Won't start." );
Expand Down Expand Up @@ -316,30 +315,51 @@ namespace RTT {
return true;
break;
case Status::requesting:
if ( this->executePending() ) { // if all steps done,
this->requestNextState();
this->executePending(); // execute steps of next state
TRACE("Is active now.");
smStatus = Status::active;
// finish entry and if finished, run the run program:
if ( this->executePreCheck() == false) {
TRACE("Yielding Entry program...");
break;
}
TRACE("Is active now.");
smStatus = Status::active;
// check transitions:
this->requestNextState();
// post-check transitioning:
if ( this->executePostCheck() == false) {
TRACE("Yielding ...");
break;
}
break;
case Status::active:
this->executePending();
this->executePreCheck();
break;
case Status::running:
if ( this->executePending() == false) {
TRACE("Yielding...");
// finish entry and if finished, run the run program:
if ( this->executePreCheck() == false) {
TRACE("Yielding Entry program...");
break;
}
// check transitions:
this->requestNextState();
// post-check transitioning:
if ( this->executePostCheck() == false) {
TRACE("Yielding ...");
break;
}
// if all pending done:
this->requestNextState(); // one state at a time
this->executePending(); // execute steps of next state
break;
case Status::paused:
if (mstep) {
if ( this->executePending(true) ) { // if all steps done,
this->requestNextState(true); // one state at a time
this->executePending(true); // execute steps of next state
// finish entry and if finished, run the run program:
if ( this->executePreCheck(true) == false) {
TRACE("Yielding Entry program...");
break;
}
// check transitions:
this->requestNextState(true);
// post-check transitioning:
if ( this->executePostCheck(true) == false) {
TRACE("Yielding ...");
break;
}
TRACE("Did a step.");
mstep = false;
Expand Down Expand Up @@ -445,7 +465,7 @@ namespace RTT {
TRACE("Transition triggered from '"+ (current ? current->getName() : "null") +"' to '"+(newState ? newState->getName() : "null")+"'.");
// reset handle and run, in case it is still set ( during error
// or when an event arrived ).
currentRun = 0;
//currentRun = 0;
currentHandle = 0;
if ( transProg ) {
transProg->reset();
Expand All @@ -461,11 +481,6 @@ namespace RTT {
leaveState(current);
assert( currentEntry == 0); // we assume that in executePending, trans and exit get executed first and entry is stil null.
}
// schedule a run for the next 'step'.
// if handle above finished, run will be called directly
// in executePending. if handle was not finished
// or stepping, it will be called after handle.
runState( newState );
}

void StateMachine::enableGlobalEvents( )
Expand Down Expand Up @@ -633,7 +648,7 @@ namespace RTT {
if( current == 0 )
return 0;
// only a run program may be interrupted...
if ( !interruptible() || currentTrans ) {
if ( !interruptible() || currentTrans || current != next) {
return current; // can not accept request, still in transition.
}

Expand Down Expand Up @@ -994,20 +1009,19 @@ namespace RTT {
}

bool StateMachine::executePending( bool stepping )
{
return executePreCheck(stepping) && executePostCheck(stepping);
}

bool StateMachine::executePreCheck( bool stepping )
{
TRACE_INIT();
// This function has great resposibility, since it acts like
// a scheduler for pending requests. It tries to devise what to
// do on basis of the contents of variables (like current*, next,...).
// This is a somewhat
// fragile implementation but requires very little bookkeeping.
// if returns true : a transition (exit/entry) is done
// and a new state may be requested.

if ( inError() )
return false;

// first try to finish the current entry program, if any:
// TRACE("executePreCheck..." );
// first try to finish the current entry program (this only happens if entry was not atomically implemented, ie yielding):
if ( currentEntry ) {
TRACE("Executing entry program of '"+ (current ? current->getName() : "(null)") +"'" );
if ( this->executeProgram(currentEntry, stepping) == false )
Expand All @@ -1021,6 +1035,25 @@ namespace RTT {
}
}

// Run is executed before the transitions.
if ( currentRun ) {
TRACE("Executing run program of '"+ current->getName() +"'" );
if ( this->executeProgram(currentRun, stepping) == false )
return true;
// done.
TRACE("Finished run program of '"+ (current ? current->getName() : "(null)") +"'" );
}
return true;
}

bool StateMachine::executePostCheck( bool stepping )
{
TRACE_INIT();

if ( inError() )
return false;

// TRACE("executePostCheck..." );
// if a transition has been scheduled, proceed directly instead of doing a run:
if ( currentTrans ) {
TRACE("Executing transition program from '"+ (current ? current->getName() : "(null)") + "' to '"+ ( next ? next->getName() : "(null)")+"'" );
Expand Down Expand Up @@ -1074,7 +1107,12 @@ namespace RTT {

current = next;
enterState(next);
runState(next);
enableEvents(next);
} else {
// schedule a new run of the current state
if (current)
runState(current);
}

// finally, execute the current Entry of the new state:
Expand Down Expand Up @@ -1109,18 +1147,6 @@ namespace RTT {
}
}

// Run is executed before the transitions.
if ( currentRun ) {
TRACE("Executing run program of '"+ (current ? current->getName() : "(null)") +"'" );
if ( this->executeProgram(currentRun, stepping) == false )
return false;
// done.
TRACE("Finished run program of '"+ (current ? current->getName() : "(null)") +"'" );
// in stepping mode, delay 'true' one executePending().
if ( stepping )
return false;
}

return true; // all pending is done
}

Expand Down Expand Up @@ -1216,7 +1242,7 @@ namespace RTT {
if ( !inError() ) {
enableEvents(current);

if ( this->executePending() ) {
if ( this->executePreCheck() ) {
smStatus = Status::active;
TRACE("Activated.");
} else {
Expand Down
25 changes: 17 additions & 8 deletions rtt/scripting/StateMachine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,19 +380,28 @@ namespace RTT
bool requestStateChange( StateInterface * s_n );

/**
* Execute any pending State (exit, entry, handle) programs.
* You must executePending, before calling requestState() or
* requestNextState(). You should only call requestState() or requestNextState()
* if executePending returns true.
* Returns executePreCheck() && executePostCheck()
*/
bool executePending(bool stepping = false);

/**
* Execute only 'yielded entry' and run state programs before any state transitions are checked.
*
* Due to the pending requests, the currentState() may have changed.
* @param stepping provide true if the pending programs should
* be executed one step at a time.
* @retval true if run was running or pending.
* @retval false only if the entry program is yielding.
*/
bool executePreCheck( bool stepping = false );
/**
* Execute only transition, handle or exit/entry state programs after any state transitions are checked.
*
* @param stepping provide true if the pending programs should
* be executed one step at a time.
* @retval true if nothing was pending @retval false if there was
* some program executing.
* @retval true if nothing was pending
* @retval false if there was some program executing.
*/
bool executePending( bool stepping = false );
bool executePostCheck( bool stepping = false );

/**
* Express a precondition for entering a state. The
Expand Down
26 changes: 21 additions & 5 deletions tests/state_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* (at your option) any later version. *
* *
***************************************************************************/

#define ORO_SIGNALLING_OPERATIONS
#include "unit.hpp"

#include <rtt-config.h>
Expand Down Expand Up @@ -827,7 +827,8 @@ BOOST_AUTO_TEST_CASE( testStateYieldbySend )
+ " initial state INIT {\n"
+ " var double d = 0.0\n"
+ " run { do o_event.send(1.0); test.i = 5; do test.assert(test.i == 5);\n" // asynchronous send on o_event, so signal must be processed when we return.
+ " do yield;\n"
+ " do yield;\n" // o_event still in the message queue
+ " do yield;\n" // o_event should have been processed.
+ " test.i = 10;\n"
+ " do test.assert(false); }\n"
+ " transition o_event(d) select NEXT;\n"
Expand Down Expand Up @@ -1077,10 +1078,25 @@ BOOST_AUTO_TEST_CASE( testStateOperationSignalTransition )
// test event reception from own component
string prog = string("StateMachine X {\n")
+ " var double et = 0.0\n"
+ " var int cnt = 0, check_exit = 0, check_exit2 = 0, check_run = 0, check_entry = 0, check_trans = 0, check_trans2 = 0\n"
+ " initial state INIT {\n"
+ " transition o_event(et) { test.assert(et == 3.33); } select FINI\n" // test signal transition
+ " }\n"
+ " final state FINI { entry { test.assert( et == 3.33);} } \n"
+ " transition o_event(et) { cnt=cnt+1; check_trans = cnt; test.assert(et == 3.33); } select CHECKER\n" // test signal transition
+ " exit { cnt=cnt+1; check_exit = cnt; }\n"
+ " }\n"
+ " state CHECKER {\n"
+ " entry { cnt=cnt+1; check_entry = cnt; test.assert( et == 3.33); } \n"
+ " run { cnt=cnt+1; check_run = cnt; }\n"
+ " exit { cnt=cnt+1; check_exit2 = cnt; }\n"
+ " transition { cnt=cnt+1; check_trans2 = cnt; } select FINI\n"
+ " }\n"
+ "final state FINI { entry {\n"
+ " test.assertEqual( check_trans, 1 );\n"
+ " test.assertEqual( check_exit, 2 );\n"
+ " test.assertEqual( check_entry, 3 );\n"
+ " test.assertEqual( check_run, 4 );\n"
+ " test.assertEqual( check_trans2, 5 );\n"
+ " test.assertEqual( check_exit2, 6 );\n"
+ " } } \n"
+ "}\n"
+ "RootMachine X x()\n";
this->parseState( prog, tc );
Expand Down

0 comments on commit bec07dd

Please sign in to comment.