====== Define your own Sequence or Step ======
A sequence or step must be carefully designed in order to run as expected.
===== Write your own Class =====
Extend the class //Sequence// or //Step//, respectively. A step comprises a single operation and never runs concurrently to its caller. A sequence typically includes two or more steps and can run in parallel to its caller.
class MyStep : public Step {
public:
MyStep(std::string name, Sequence* caller) : Step(name, caller) {...}
...
};
class MySequence : public Sequence {
public:
MySequence(std::string name, Sequence* caller) : Sequence(name, caller, false) {...}
...
};
Your constructor usually includes a name for the sequence or step, a reference to the caller of this object. The latter point is very important, because every step or sequence must know its caller or owner. \\
You then initialize the sequence in the initializer list by writing ''Sequence(name, caller, false)''. The last parameter defines whether your sequence is blocking or nonblocking. Setting to blocking (''true'') makes this sequence block its calling sequence, setting to blocking (''false'') starts a new thread which runs the sequence in parallel to its calling sequence. A step is always blocking.
In the body of your constructor you define the attributes of your sequence. This includes:
* monitors with conditions: you can add several monitors which check for various conditions.
* monitor property: every monitor can have its own exception behavior. This behavior determines how the sequence behaves after a monitor fires.
* exception sequence: A special exception sequence can run after a monitor fires.
The following example shows how these attributes can be set.
class MySequence : public Sequence {
public:
MySequence(std::string name, Sequence* caller) : Sequence(name, caller, false) {
// this sequence will run in its own thread and concurrently to its caller
setTimeoutTime(2.5); // the built-in timeout monitor has its timeout condition set to 2.5s
setTimeoutExceptionSequence(excSeq); // when the monitor fires, this exception sequence will run
setTimeoutBehavior(SequenceProp::abort); // after exception handling the remaining steps of this sequence will be aborted
}
};
If you define a main sequence which has no calling sequence and which must be nonblocking per default, you can make use of another constructor by calling
class MainSequence : public Sequence {
public:
MainSequence(std::string name, Sequencer& seq) : Sequence(name, seq) {...}
...
};
===== Define the Action =====
Implement the function //action()//. This comprises the work which should be done. In case of a sequence this might be a sequence of steps. In case of a step it might be setting a new set position. Please make sure that this function does not block! Never wait for some condition to be reached nor wait for some time to elapse (no sleeping). Waiting for conditions must be done with the help of [[eeros_architecture:sequencer:monitors|]].
public:
int action() {
step1();
step2();
step3();
return 0;
}
Its mandatory to implement this function. If not, no work is done and the step or sequence terminates immediately.
A step or blocking sequence can return a value of type ''int''. This can be useful to hand back basic information to the calling sequence. \\
There might be sequences which should never stop. This must be done as shown below:
public:
int action() {
while (state == SequenceState::running) step1();
// while (true) step1(); // WRONG!
return 0;
}
This guarantees that the sequence could be aborted, resumed or restarted due to exception sequences.
===== Define Preconditions =====
You may want to start a sequence or step only if a certain precondition is met. Override the function //checkPreCondition()//, e.g.:
bool checkPreCondition() {
return robot.buttonPressed();
}
If the check returns ''false'' the sequence or step will be omitted and control will return to its caller.
===== Add Parameters =====
It is possible to pass parameters into sequences or steps. This can be very convenient and allows a sequence handle a given task in a flexible way. Consider the following examples:
move(1.0, 2.0); // calls a step which sets the position to x=1.0/y=2.0 meters
move(2.0, 2.5); // calls the same step which now sets the position to x=2.0/y=2.5 meters
Parameter passing is possible by implementing the ()-operator with a given set of parameters. For the above example this will be done with:
int operator() (double x, double y) {
this->x = x; // store the first parameter into a local variable for further use
this->y = y; // store the second parameter into a local variable for further use
return start(); // this will start the step or sequence
}
Do not forget to call //start()// at the end. If you don't need parameters you simply use the default ()-operator which already includes the call to //start()//. \\
It is possible to implement more than one ()-operator if you wish to pass different sets of parameters.
===== Define Exit Conditions =====
A sequence or step only ends after its exit condition is met. Typically you wait for a position be be reached or a time to be met. Override the function //checkPreExitCondition()//, e.g.:
bool checkExitCondition() {
return robot.x >= 13.8 && robot.y >= 2.4;
}
If the check returns ''false'' the sequence or step will remain in ''running'' state and the check is periodically repeated until it returns ''true''. \\
Here again, please make sure not to write something like
bool checkExitCondition() {
while (robot.x < 13.8 || robot.y < 2.4); // wrong, do not wait here until condition is met!
return true;
}
Never wait while checking for a condition. The control must be returned immediately to the sequencer. The sequencer itself will make sure that it continues only after the exit condition is met.
===== Waiting in Sequences or Steps =====
As mentioned before you should never wait by using //sleep// in a action method. However, quite often it is desirable to wait for some time to pass when running sequences. How to do properly? Use the predefined step ''Wait''. Study the examples in [[getting_started:tutorials:start|]]. Contrary to a simple //sleep// it does not block the sequencer and the checking of monitors of this step or sequence continues unhindered.