This is an old revision of the document!
Sequencer Usage
This page describes the usage of the sequencer in the EEROS framework.
Sequencer
Create an instance of the sequencer. If its the first sequencer you define, it will automatically be the main sequencer.
int main() { Sequencer mainSequencer(); // define sequencer Sequence mySequence("My Sequence", &mainSequencer); // define sequence mainSequencer.start(&mySequence); // start the sequencer mainSequencer.join(); // wait for the sequencer to finish }
Each sequencer runs in its own thread of execution. It therefore can be in exactly one of the following states: executing, waiting, terminating, terminated, idle. Further, a sequencer runs either in automatic mode or it can be in stepping mode. Upon creation the thread runs and the state of the sequencer is idle.
The next step is to install a meaningfull sequence (see below Sequence). After this you can start the sequencer with start(). This sets its state to executing. The main program may terminate when the main sequencer has terminated. For this purpose it has to wait for the sequencer thread with join().
If it is necessary to keep data which is specific to a certain sequencer you may define your own class MySequencer as an extension of Sequencer and define the data therein.
class MySequencer : public eeros::sequencer::Sequencer
Sequence
A sequence is a well defined course of steps. These steps have to be defined by the user in its run method. A user has to derive a class MySequence from Sequence:
class MySequence : public eeros::sequencer::Sequence<>
As a next step you have to create an instance of MySequence. By doing so you can choose a name and assign this sequence to a sequencer.
MySequence mySequence("My Sequence", &mainSequencer); // define sequence
The sequencer will then run through the sequence as follows:
init(); yield(); if(!checkPreCondition()) return SequenceResult<void>(result::preConditionFailure); yield(); run(); yield(); if(!checkPostCondition()) return SequenceResult<void>(result::postConditionFailure); yield(); exit(); return SequenceResult<void>(result::success);
Every sequence starts with an init function. You have to implement this function in your derived class. As a next step you can check for preconditions for this sequence to be met. If the test fails the sequence will immediately stop. Here again, make sure to implement your own check function.
The main work of the sequence is done in the function run(). At the end every sequence checks for possible post condition violations and runs its exit() function. Let's look at a simple example.
class MySequence : public Sequence<> { public: MySequence(std::string name, Sequencer* seq, Robot& r) : Sequence<>(name, seq), robot(r){ } void init() { log.trace() << "Sequence '" << getName() << "': " << "Init running"; sleep(1); } bool checkPreCondition() { log.trace() << "Checking precondition"; if(robot.getPosZ() < 10) return true; return false; } void run() { robot.moveXY(10, -20); yield(); robot.moveXY(-10, -30); } bool checkPostCondition() { log.trace() << "Checking postcondition"; if(robot.getPosX() < 0) return true; return false; } void exit() { log.trace() << "Sequence '" << getName() << "': " << "Exit running"; sleep(1); } private: Robot& robot; };
The sequence moves a hypothetical robot in x,y,z direction. Every step of the sequence in the run() function might be followed by a call to yield(). While this could be ommitted it allows the seqeuncer to pause in case that the sequencer is run in step mode. This can be very handy while teaching a robot or for debugging purposes.
Subsequence
A subsequence is a sequence which is called by another sequence. Such a subsequence can be called in a blocking or non-blocking way. Blocking means that the step waits (or blocks) until the subsequence has finished. Non-blocking means that subsequence and main sequence run concurrently.
Blocking Call of a Subsequence
If you want to save your own data in the sequence, you need to create a new instance of a sequence. This should be done in the desired step (method) of the superior sequence. e.g. in the method MoveBlocking()
MyBlockingSubSequence* subSequence = dynamic_cast<MyBlockingSubSequence*>eeros::sequencer::Sequence::getSequence("BlockingSubSequence")); if(!subSequence){ //callerThread for Blocking Sub Sequence is the same as is in this running Sequence. subSequence = new MyBlockingSubSequence("BlockingSubSequence", callerThread); }
Note:
- In the above lines only one object of MyBlockingSubSequence with the name BlockingSubSequence is created.
- It is not allowed to call the callerThread.addRunnable(this) method, because the superior sequence calls the blocking sub-sequence directly by the run() method.
To start the sub-sequence just call run(), which calls all methods in the callback list (filled by fillCallBacks()). Be sure that the sub-sequence is terminated before you restart it. This could happen, for example, in an exception handling ( Error Handler ).
//Here we wait for the returning of the subSequence.run() method while(subSequence->getState() != eeros::sequencer::kSequenceFinished){ subSequence->run(); }
Non Blocking Call of a Sub-Sequence
Remark: Please use pointer for a non blocking call, because the memory referenced by a pointer to an object exists until you delete it However, an object is removed as soon the scope of the method is no longer being used. In the case where both sequences (sequence and sub-sequence) run contemporaneously, the sub-sequence should not be deleted at the end of the step (method). That's why you should use pointers!
A non-blocking sub-sequence has to be created as a thread, so you need a sub-sequencer for starting the new thread. e.g.:
MySequencer* subSequencer = new MySequencer("SubSequencer");
Here you can reuse an existing sub-sequence if you want.
MyNonBlockingSubSequence* subSequence = dynamic_cast<MyNonBlockingSubSequence*>(eeros::sequencer::Sequence::getSequence("NonBlockingSubSequence")); if(!subSequence){ subSequence = new MyNonBlockingSubSequence("NonBlockingSubSequence",subSequencer); }
Note:
- The constructor of the MyNonBlockingSequence class has to call callerThread.addRunnable(this) to add itself to the sub-sequencer runnable list, or else the callback methods are not called by the sub-sequencer.
- Do not forget to call callerThread.stop() in the last step of the sub-sequence.
As soon as you have created the sub-sequence, you can start the sub-sequencer, which creates the threads to run all steps.
subSequencer->start();
In an other step of the superior sequence you can wait until the non-blocking sequence is terminated by using the method: ExecutorService::waitForSequenceEnd(…);.
//Here we wait for the subsequencer Thread eeros::sequencer::Sequencer* seq = eeros::sequencer::Sequencer::getMainSequencer()->findSequencer("SubSequencer"); if(seq && seq->getStatus() != kStopped){ ExecutorService::waitForSequenceEnd(seq); }