diff --git a/doc/xml/images/Master-Slave-Semantics.svg b/doc/xml/images/Master-Slave-Semantics.svg new file mode 100644 index 000000000..1ccfeecbc --- /dev/null +++ b/doc/xml/images/Master-Slave-Semantics.svg @@ -0,0 +1,673 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Master-Slave Execution Order + + Update step + + + OwnThreadOperations + EventPortCallbacks + + ScriptingFunctions + + updateHook() + + + + + + + MASTER + + + TaskContextExecutionEngine + + + + ACTIVITY + + + + Update step + + + OwnThreadOperations + EventPortCallbacks + + ScriptingFunctions + + updateHook() + + + + + + SLAVE + + + TaskContextExecutionEngine + + + + SlaveActivity + + + + update() ( from C++ ) + + TaskContext Interface + + + + + TaskContext Interface + + + + update() ( from a script ) + + + + diff --git a/doc/xml/images/OperationCallvsSend.svg b/doc/xml/images/OperationCallvsSend.svg new file mode 100644 index 000000000..196dc4176 --- /dev/null +++ b/doc/xml/images/OperationCallvsSend.svg @@ -0,0 +1,1349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + Thread ofComponent A + + + call() : executed immediately ! + + OwnThreadOperation of B + + Thread ofComponent B + + + script or updateHook() + + + script or updateHook() + time + time + + + Thread ofComponent A + + + send() : executed immediately ! + OwnThreadOperation of B + + Thread ofComponent B + + + script or updateHook() + + time + time + + Blocks + + + script or updateHook() + + Operations: call() versus send() + Continues + + Continues + + Continues + + + + script or updateHook() + + + + script or updateHook() + + + + send() : delayed until updateHook finishes ! + OwnThreadOperation of B + + + + script or updateHook() + + Continues + + + + Executes + + + + Executes + + Continues + + + + Thread ofComponent A + Thread ofComponent B + + + script or updateHook() + + time + time + + + ClientThreadOperation of B + + + + Executes + Continues + + + + script or updateHook() + + + + call() : delayed until updateHook finishes ! + + + + script or updateHook() + + + Blocks + OwnThreadOperation of B + + + script or updateHook() + + + ClientThreadOperation of B + + + + + Executes + Continues + + + + + script or updateHook() + + ClientThread does not influence thread of Component BBUT must be implemented thread-safe : use mutexesto protect reading/writing data from Component B ! + + diff --git a/doc/xml/images/StateMachineFlow.svg b/doc/xml/images/StateMachineFlow.svg new file mode 100644 index 000000000..ccccb959b --- /dev/null +++ b/doc/xml/images/StateMachineFlow.svg @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + entry Program + + + + (sleep) + + + + run Program + + + + check Transitions + + + + exit Program + + + + handle Program + + + + transition Program + + + + + + + + + + + No transition matches + Transitionmatches + + Transitionto other state + Transitionto same state(self-transition) + + + Orocos Scripting State Machines (RTT v2.9) + This diagram shows all possible stepsin an Orocos State Machine. If a certain programis not present in a state, it is skipped and theState Machine goes on to the next step. + Here we switch from one stateto another state + + diff --git a/doc/xml/images/StateMachineSteps-SleepAfterEntry.svg b/doc/xml/images/StateMachineSteps-SleepAfterEntry.svg new file mode 100644 index 000000000..10973f047 --- /dev/null +++ b/doc/xml/images/StateMachineSteps-SleepAfterEntry.svg @@ -0,0 +1,751 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + entry Program + + + + + run Program + + + + CheckTransitions + + + + + exit Program + + + + + run Program + + + + + CheckTransitions + + + + + + entry Program + + + + First match cause toselect 'state B' + initial state + + + + + + + + Steps + 0 + 1 + 2 + (sleep in state A) + (sleep in state B) + (sleep in state B) + . . . + state A + + state A + + state B + + state B + + + + No match cause tohandle + + + handle Program + + + + + + run Program + + + + + CheckTransitions + + + + state B + + + (sleep ... ) + 3 + state B + + Orocos State Machine transitions example (RTT v2.9) + This example shows in which step which programs of which states are executed.Once for a step where a transition to a next state succeeds ( from A to B in step 1),and once for a step where no transition to another state succeed ( stay in B in step 2).If a certain program is not present in a state, it is treated as an empty program. + + diff --git a/doc/xml/images/StateMachineSteps-SleepAfterRun.svg b/doc/xml/images/StateMachineSteps-SleepAfterRun.svg new file mode 100644 index 000000000..95f6c2e93 --- /dev/null +++ b/doc/xml/images/StateMachineSteps-SleepAfterRun.svg @@ -0,0 +1,747 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + entry Program + + + + run Program + + + + CheckTransitions + + + + exit Program + + + + run Program + + + + + CheckTransitions + + + + + entry Program + + + + First match cause toselect 'state B' + initial state + + + + + + + + Steps + 0 + 1 + 2 + (sleep in state A) + (sleep in state B) + (sleep in state B) + . . . + state A + + state A + + state B + + state B + + + + No match cause tohandle + + + handle Program + + + + + run Program + + + + + CheckTransitions + + + state B + + + (sleep ... ) + 3 + state B + + Orocos State Machine transitions example (RTT v2.8) + This example shows in which step which programs of which states are executed.Once for a step where a transition to a next state succeeds ( from A to B in step 1),and once for a step where no transition to another state succeed ( stay in B in step 2).If a certain program is not present in a state, it is treated as an empty program. + + diff --git a/package.xml b/package.xml index fc29d7668..0a2b103af 100644 --- a/package.xml +++ b/package.xml @@ -17,11 +17,13 @@ boost omniorb pkg-config + log4cpp boost omniorb xpath-perl pkg-config + log4cpp cmake diff --git a/rtt/PropertyBag.cpp b/rtt/PropertyBag.cpp index 6bf27465d..71ae72838 100644 --- a/rtt/PropertyBag.cpp +++ b/rtt/PropertyBag.cpp @@ -615,7 +615,7 @@ namespace RTT temp->update( (*sit) ); } // step 3 : add result to target bag. - target.add( temp ); + target.ownProperty( temp ); } } ++it; diff --git a/rtt/marsh/CPFDemarshaller.cpp b/rtt/marsh/CPFDemarshaller.cpp index 86992c253..a5af978b4 100644 --- a/rtt/marsh/CPFDemarshaller.cpp +++ b/rtt/marsh/CPFDemarshaller.cpp @@ -143,10 +143,10 @@ namespace RTT if ( type == "boolean" ) { if ( value_string == "1" || value_string == "true") - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, true ) ); else if ( value_string == "0" || value_string == "false" ) - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, false ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ @@ -154,27 +154,27 @@ namespace RTT } else if ( type == "char" ) { if ( value_string.empty() ) - bag_stack.top().first->add( new Property( name, description, '\0' ) ); + bag_stack.top().first->ownProperty( new Property( name, description, '\0' ) ); else if ( value_string.length() != 1 ) throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain a single character, got '"+ value_string +"'.").c_str()); else - bag_stack.top().first->add( new Property( name, description, value_string[0] ) ); + bag_stack.top().first->ownProperty( new Property( name, description, value_string[0] ) ); } else if ( type == "uchar" || type == "octet" ) { if ( value_string.length() != 1 ) throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain a single unsigned character, got '"+ value_string +"'.").c_str()); else - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, value_string[0] ) ); } else if ( type == "long" || type == "short") { int v; if ( sscanf(value_string.c_str(), "%d", &v) == 1) - bag_stack.top().first->add( new Property( name, description, v ) ); + bag_stack.top().first->ownProperty( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain an integer value, got '"+ value_string +"'.").c_str()); @@ -183,7 +183,7 @@ namespace RTT { unsigned int v; if ( sscanf(value_string.c_str(), "%u", &v) == 1) - bag_stack.top().first->add( new Property( name, description, v ) ); + bag_stack.top().first->ownProperty( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain an integer value, got '"+ value_string +"'.").c_str()); @@ -192,7 +192,7 @@ namespace RTT { long long v; if ( sscanf(value_string.c_str(), "%lld", &v) == 1) - bag_stack.top().first->add( new Property( name, description, v ) ); + bag_stack.top().first->ownProperty( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain an integer value, got '"+ value_string +"'.").c_str()); @@ -201,7 +201,7 @@ namespace RTT { unsigned long long v; if ( sscanf(value_string.c_str(), "%llu", &v) == 1) - bag_stack.top().first->add( new Property( name, description, v ) ); + bag_stack.top().first->ownProperty( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain an integer value, got '"+ value_string +"'.").c_str()); @@ -210,7 +210,7 @@ namespace RTT { double v; if ( sscanf(value_string.c_str(), "%lf", &v) == 1 ) - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ @@ -220,14 +220,14 @@ namespace RTT { float v; if ( sscanf(value_string.c_str(), "%f", &v) == 1 ) - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, v ) ); else throw SAXException(std::string("Wrong value for property '"+type+"'." \ " Value should contain a float value, got '"+ value_string +"'.").c_str()); } else if ( type == "string") - bag_stack.top().first->add + bag_stack.top().first->ownProperty ( new Property( name, description, value_string ) ); tag_stack.pop(); value_string.clear(); // cleanup @@ -240,7 +240,7 @@ namespace RTT { Property* prop = bag_stack.top().second; bag_stack.pop(); - bag_stack.top().first->add( prop ); + bag_stack.top().first->ownProperty( prop ); //( new Property( pn, description, *pb ) ); //delete pb; tag_stack.pop(); diff --git a/rtt/scripting/CallFunction.hpp b/rtt/scripting/CallFunction.hpp index 76e627a5d..0120bb3b0 100644 --- a/rtt/scripting/CallFunction.hpp +++ b/rtt/scripting/CallFunction.hpp @@ -90,6 +90,7 @@ namespace RTT ~CallFunction() { this->reset(); + delete minit; } virtual bool execute() { diff --git a/rtt/scripting/CmdFunction.hpp b/rtt/scripting/CmdFunction.hpp index bd23a1eab..100da4bd1 100644 --- a/rtt/scripting/CmdFunction.hpp +++ b/rtt/scripting/CmdFunction.hpp @@ -54,6 +54,7 @@ namespace RTT ~CmdFunction() { this->reset(); + delete minit; } virtual SendStatus get() const { diff --git a/rtt/scripting/StateMachine.cpp b/rtt/scripting/StateMachine.cpp index 49dd9e26f..bb1536bda 100644 --- a/rtt/scripting/StateMachine.cpp +++ b/rtt/scripting/StateMachine.cpp @@ -267,13 +267,14 @@ namespace RTT { bool StateMachine::execute() { TRACE_INIT(); - os::MutexLock lock(execlock); + // before dealing with transitional states, // check if we're actually running. if (smStatus == Status::inactive || smStatus == Status::unloaded) { smpStatus = nill; return true; } + os::MutexLock lock(execlock); #ifdef ASSERT_PROPER_EXECUTION_CYCLES // we're loaded. Do some sanity checking. @@ -476,7 +477,7 @@ namespace RTT { } else { - TRACE("Transition triggered from '"+ (current ? current->getName() : "null") +"' to '"+(newState ? newState->getName() : "null")+"'."); + 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; @@ -487,7 +488,6 @@ namespace RTT { smStatus = Status::error; } - next = newState; currentTrans = transProg; next = newState; @@ -1035,14 +1035,14 @@ namespace RTT { if ( inError() ) return false; - TRACE("executePreCheck..." ); +// 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->getName() +"'" ); + TRACE("Executing entry program of '"+ (current ? current->getName() : "(null)") +"'" ); if ( this->executeProgram(currentEntry, stepping) == false ) return false; // done. - TRACE("Finished entry program of '"+ current->getName() +"'" ); + TRACE("Finished entry program of '"+ (current ? current->getName() : "(null)") +"'" ); // in stepping mode, delay 'true' one executePending(). if ( stepping ) { currentProg = currentRun; @@ -1052,7 +1052,7 @@ namespace RTT { // Run is executed before the transitions. if ( currentRun ) { - TRACE("Executing run program of '"+ current->getName() +"'" ); + TRACE("Executing run program of '"+ (current ? current->getName() : "(null)") +"'" ); if ( this->executeProgram(currentRun, stepping) == false ) return true; // done. @@ -1068,7 +1068,7 @@ namespace RTT { if ( inError() ) return false; - TRACE("executePostCheck..." ); +// 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)")+"'" ); @@ -1090,7 +1090,7 @@ namespace RTT { // last is exit if ( currentExit ) { - TRACE("Executing exit program from '"+ current->getName() + "' (going to '"+ (next ? next->getName() : "(null)") +"')" ); + TRACE("Executing exit program from '"+ (current ? current->getName() : "(null)") + "' (going to '"+ (next ? next->getName() : "(null)") +"')" ); if ( this->executeProgram(currentExit, stepping) == false ) return false; // done. @@ -1132,7 +1132,7 @@ namespace RTT { // finally, execute the current Entry of the new state: if ( currentEntry ) { - TRACE("Executing entry program of '"+ current->getName() +"'" ); + TRACE("Executing entry program of '"+ (current ? current->getName() : "(null)") +"'" ); if ( this->executeProgram(currentEntry, stepping) == false ) return false; // done. @@ -1150,7 +1150,7 @@ namespace RTT { // Handle is executed after the transitions failed. if ( currentHandle ) { - TRACE("Executing handle program of '"+ current->getName() +"'" ); + TRACE("Executing handle program of '"+ (current ? current->getName() : "(null)") +"'" ); if ( this->executeProgram(currentHandle, stepping) == false ) return false; // done. @@ -1233,6 +1233,7 @@ namespace RTT { TRACE("Won't activate: already active."); return false; } + os::MutexLock lock(execlock); smpStatus = nill; @@ -1262,8 +1263,6 @@ namespace RTT { // execute the entry program of the initial state. if ( !inError() ) { - enableEvents(current); - if ( this->executePreCheck() ) { smStatus = Status::active; TRACE("Activated."); @@ -1300,9 +1299,10 @@ namespace RTT { currentTrans = 0; // if we stalled, in previous deactivate // even skip/stop exit program. - if ( next != 0 && current ) + if ( next != 0 && current ) { leaveState( current ); - else { + disableEvents(current); + } else { currentExit = 0; currentTrans = 0; } diff --git a/rtt/scripting/StateMachineService.cpp b/rtt/scripting/StateMachineService.cpp index a70c446cf..d844b2afd 100644 --- a/rtt/scripting/StateMachineService.cpp +++ b/rtt/scripting/StateMachineService.cpp @@ -103,10 +103,11 @@ namespace RTT if (instantiate) { // Remove any remaining SendHandleAlias attributes, since they are not allowed for an instantiate... // See SendHandleAlias::copy() for more details. - for ( ConfigurationInterface::map_t::iterator it = values.begin(); it != values.end(); ++it) { + for ( ConfigurationInterface::map_t::iterator it = values.begin(); it != values.end(); ) { if (dynamic_cast(*it)) { - values.erase(it); - it = values.begin(); + it = values.erase(it); + } else { + ++it; } } } diff --git a/tests/corba_test.cpp b/tests/corba_test.cpp index 7a3193bda..7e30089db 100644 --- a/tests/corba_test.cpp +++ b/tests/corba_test.cpp @@ -46,7 +46,10 @@ class CorbaTest : public OperationsFixture pint1("pint1", "", 3), pdouble1(new Property("pdouble1", "", -3.0)), aint1(3), adouble1(-3.0), wait(0) { - // connect DataPorts + // check operations (moved from OperationCallerComponent constructor for reuseability in corba-ipc-server) + BOOST_REQUIRE( caller->ready() ); + + // connect DataPorts mi1 = new InputPort ("mi"); mo1 = new OutputPort ("mo"); diff --git a/tests/operations_fixture.cpp b/tests/operations_fixture.cpp index c6a9308ba..2202dfee7 100644 --- a/tests/operations_fixture.cpp +++ b/tests/operations_fixture.cpp @@ -49,11 +49,6 @@ class OperationCallerComponent : public TaskContext opc3 = tc->provides("methods")->getOperation("m3"); opc3.setCaller( this->engine() ); - BOOST_REQUIRE( opc0.ready() ); - BOOST_REQUIRE( opc1.ready() ); - BOOST_REQUIRE( opc2.ready() ); - BOOST_REQUIRE( opc3.ready() ); - // four combinations are possible: this->addOperation("o0", &OperationCallerComponent::m0, this, OwnThread); this->addOperation("o1", &OperationCallerComponent::m1, this, ClientThread); @@ -71,6 +66,17 @@ class OperationCallerComponent : public TaskContext // but do set the caller to this component. } + bool ready() { + BOOST_REQUIRE( opc0.ready() ); + BOOST_REQUIRE( opc1.ready() ); + BOOST_REQUIRE( opc2.ready() ); + BOOST_REQUIRE( opc3.ready() ); + return opc0.ready() && + opc1.ready() && + opc2.ready() && + opc3.ready(); + } + // plain argument tests: double m0(void) { return opc0(); } double m1(int i) { return opc1(i); } diff --git a/tests/state_test.cpp b/tests/state_test.cpp index 3bdde97db..d5cade62f 100644 --- a/tests/state_test.cpp +++ b/tests/state_test.cpp @@ -15,7 +15,7 @@ * (at your option) any later version. * * * ***************************************************************************/ -#define ORO_SIGNALLING_OPERATIONS + #include "unit.hpp" #include @@ -847,7 +847,7 @@ BOOST_AUTO_TEST_CASE( testStateYieldbySend ) + " do test.assert(false); }\n" + " transition o_event(d) select NEXT;\n" + " transitions {\n" - + " select FINI\n" + + " if test.i == 10 then select FINI\n" + " }\n" + " }\n" + " state NEXT {\n" // Success state.