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 several steps of a sequence have to be repeated more than once, it might be worth to pack them into a separate subsequence. This subsequence offers all the features of any sequence such as init or exit functions as well as checking of pre and post conditions. However, a blocking call to a subsequence allows to use the same sequencer as the calling sequence.

Simple Example

We continue with our hypothetical robot from the example in Sequence. We first define a class for the sub- or secondary sequence as follows:

class SequenceB : public Sequence<void,double> {
public:
  SequenceB(std::string name, Sequencer* seq, Robot& r) : Sequence<void,double>(name, seq), robot(r){ }
 
  void run(double z) {
    robot.moveZ(z);
    yield();
    robot.moveZ(0);
  }
 
private: 
  Robot& robot;
};

The run() method is called with one parameter of type double. There is no return value. For this reason, our class has to extend the class sequence with the template parameters <void,double>. To keep the example as simple as possible we omit the implementation of init()/exit() functions and pre and post condition check functions.

We can now define our first or main sequence with

class SequenceA : public Sequence<> {
public:
  SequenceA(std::string name, Sequencer* seq, Robot& r) : Sequence<>(name, seq), robot(r), seqB("Seq B", seq, r) { }
 
  void run() {
    robot.moveXY(10, -20);
    sequenceB(5);
    robot.moveXY(-10, 30);
    sequenceB(5);
    robot.moveXY(0, 0);
  }
 
private: 
  Robot& robot;
  SequenceB sequenceB;
};

In our main program we declare a sequencer for the main sequence. The same sequencer will also handle the subsequence. Sequence A and B do not run concurrently.

int main() {
  Sequencer sequencer;
  SequenceA seqA("Seq A", &sequencer, robot);
  sequencerA.start(&seqA);
  sequencerA.join();
};

The following figure shows the flow of action.

Nonblocking Call of a Subsequence

A nonblocking subsequence has to run in its own thread. For this reason you need a separate subsequencer for it. Both sequences can now run concurrently. A certain step in a sequence could depend on a subsequence to have finished. For this purpose the function join() will wait for the subsequence to finish.

Simple Example

We first define a class for the sub- or secondary sequence as follows:

class SequenceB : public Sequence<> {
public:
  SequenceB(std::string name, Sequencer* seq, Robot& r) : Sequence<void,double>(name, seq), robot(r){ }
 
  void run() {
    robot.moveZ(5);
    sleep(3);
    yield();
    robot.moveZ(0);
  }
 
private: 
  Robot& robot;
};

The run() method simply moves in z direction and, after a pause, returns to the original position.

We can now define our first or main sequence with

class SequenceA : public Sequence<> {
public:
  SequenceA(std::string name, Sequencer* seq, Robot& r, Sequencer& seqB) : Sequence<>(name, seq), robot(r), seqB(seqB) { }
 
  void run() {
    robot.moveXY(10, -20);
    sequencerB.start(0U);
    robot.moveXY(-10, 30);
    sequencerB.join();
    robot.moveXY(0, 0);
  }
 
private: 
  Robot& robot;
  Sequencer& sequencerB;
};

In our main program we declare a sequencer for the main sequence. A second sequencer will run the subsequence.

int main() {
  Sequencer sequencerA, sequencerB;
  SequenceB seqB("Seq B", &sequencerB, robot);
  SequenceA seqA("Seq A", &sequencerA, robot, sequencerB);
  sequencerB.addCmdSequence(&seqB);
  sequencerA.start(&seqA);
  sequencerA.join();
};

In the second step of the sequence A the sequence is started in its own thread. After this both run concurrently. The forth step of sequence A then waits for the sequence B to finish. The following figure shows the flow of action.