Table of Contents

Sequence

Namespace
ZAux
Extends
Inherited Properties
Inherited Methods
Implements

Interface to the Zeugwerk Framework Sequence function block which defines the basic methods that have to be implemented.

To run a sequence and start it from the beginning this can be done and should be implemented by using RunAsync. There, a cancellation token can be used to check could be started successfully. A running sequence can be stopped by calling Stop method.

There are also several methods to make life easier for application engineers on waiting an aborting the sequence if an execution of an object fails like Await (waiting for one object), Await2 (waiting for two objects) and Await3 (waiting for three objects).

Note

While implementing a SetLogger method, the platform-dependent nature of logging limits the log-ability of the implementation. This is the reason why this specific object is marked as abstract and one of the following objects should be used instead.

The typical declaration of the function body of a sequence is

FUNCTION_BLOCK <UnitName>Sequence EXTENDS ZAux.Sequence
VAR
  _step : ZCore.Step(<UnitName>Step.Begin, <UnitName>Step.End); // i.e. ZCore.Step(GoHomeStep.GoHomeBegin, GoHomeStep.GoHomeEnd);
END_VAR

Here, <UnitName>Step is an enumeration that contains all steps that the sequence may attain. The enumeration is suggested for better readability, and may be replaced with numbers - for better traceability in a log we strongly recommend to use an enumeration though. The implementation looks like

IF NOT Busy THEN
  RETURN;
END_IF

IF OnStart(_step)
THEN
  ;// init custom sequence variables here ...
END_IF

IF OnStop()
THEN
  ;// external stop was called ...
END_IF

IF OnHalt()
THEN
  ;// Halting := TRUE; // Uncomment to acknowledge that the sequence is halting
END_IF

REPEAT
  LogStep();
 
  CASE _step.Index OF
    (* ------------------------------------------------------------------ *)
    <UnitName>Step.Begin:
    (* ------------------------------------------------------------------ *)
      IF _step.OnEntry()
      THEN
        ; // ...
      END_IF
       
      Await(..., nextStep:=<UnitName>Step.End);
 
    (* ------------------------------------------------------------------ *)
    <UnitName>Step.End:
    (* ------------------------------------------------------------------ *)
      SetBusy(FALSE);
   
  ELSE
    Abort('sequence contains an unhandled step');
  END_CASE
UNTIL _step.IsNotRepeatable() OR_ELSE NOT Busy END_REPEAT

If there are some actions which have to be done when the started object has finished, Await might not be the right statement here. Of course the Framework offers the classical approach by testing Error and Busy properties in an IF and ELSIF statement.

IF NOT Busy THEN
  RETURN;
END_IF

IF OnStart(_step)
THEN
  ;// init custom sequence variables here ...
END_IF

IF OnStop()
THEN
  ;// external stop was called ...
END_IF

IF OnHalt()
THEN
  ;// Halting := TRUE; // Uncomment to acknowledge that the sequence is halting
END_IF

REPEAT
  LogStep();
 
  CASE _step.Index OF
    (* ------------------------------------------------------------------ *)
    <UnitName>Step.Begin:
    (* ------------------------------------------------------------------ *)
      IF _step.OnEntry()
      THEN
        <object>.RunAsync(THIS^); // ...
      END_IF
       
      IF <object>.Error THEN
        // if object finished with an error do something else here
        _step.SetNext(nextStep:=<UnitName>Step.End);
      ELSIF NOT <object>.Busy THEN
        // if object finished successfully do something else
        _step.SetNext(nextStep:=<UnitName>Step.End);
      END_IF
 
    (* ------------------------------------------------------------------ *)
    <UnitName>Step.End:
    (* ------------------------------------------------------------------ *)
      SetBusy(FALSE);
   
  ELSE
    Abort('sequence contains an unhandled step');
  END_CASE
UNTIL _step.IsNotRepeatable() OR_ELSE NOT Busy END_REPEAT

The enumeration is usually implemented for several sequences which are based on one unit and looks like this:

{attribute 'qualified_only'}
{attribute 'to_string'}
TYPE <UnitName>Step :
(
  BootingBegin,
  BootingInitializeEquipment,
  BootingEnd,

  AutomaticBegin,
  AutomaticDoSomething,
  AutomaticEnd
);
END_TYPE

The following code is an example, which is taken from the Quickstart Tutorial. Note that the example uses a specialized version of Sequence, which supports logging.

FUNCTION_BLOCK PickerSequenceGoHome EXTENDS ZAux.Sequence
VAR
  _step : ZCore.Step(PickerStep.GoHomeBegin, PickerStep.GoHomeEnd);
  _timer : ZAux.Timer;
END_VAR
IF NOT Busy THEN
  RETURN;
END_IF

IF OnStart(_step)
THEN
  // init custom sequence variables here ...
END_IF

IF OnStop()
THEN
  // external stop was called ...
END_IF

IF OnHalt()
THEN
  // Halting := TRUE; // Uncomment to acknowledge that the sequence is halting
END_IF

REPEAT
  LogStep();
 
  CASE _step.Index OF
    (* ---------------------------------------------- *)
    PickerStep.GoHomeBegin:
    (* ---------------------------------------------- *)
      IF _step.OnEntry()
      THEN
        _timer.WaitAsync(2.0);
      END_IF
       
      Await(_timer, nextStep:=PickerStep.GoHomeEnd);
 
    (* ---------------------------------------------- *)
    PickerStep.GoHomeEnd:
    (* ---------------------------------------------- *)
      SetBusy(FALSE);
   
  ELSE
     Abort('sequence contains an unhandled step');
  END_CASE
UNTIL _step.IsNotRepeatable() OR_ELSE NOT Busy END_REPEAT

If you want to use this sequence in an actual program it has to be instantiated and called properly. Create an empty solution with a MAIN.PRG and insert the following snippet

PROGRAM MAIN
VAR
  Step : ZCore.Step(0, 100);
  Picking : PickerSequenceGoHome;
END_VAR
------------------------------------------
CASE Step.Index OF
  0:
    IF Step.OnEntry() THEN
      Picking.RunAsync(0);
    END_IF
 
    Picking.Cyclic();
    IF Picking.Error THEN
      Step.SetNext(99);
    ELSIF NOT Picking.Busy THEN
      Step.SetNext(20);
    END_IF
   
  20: // Idle
    ;
   
  99: // Error
    ;
 
END_CASE
Note

When using this function block it is strongly recommended to overwrite StepDecoded such that the logging mechanism of the object can write strings for distinct steps of the sequence instead of mere numbers.

Note

A sequence is also an ExecutionToken to be used as a parameter for async calls (e.g. _axis.AxisName.MoveAbsolutAsync(THIS^)). A sequence as an ExecutionToken gets informed (aborted) from an object if the originally started task has changed unexpected.

Note

A sequence cannot be recovered.

This implementation extends the abstract (Sequence)[ZCore.Sequence] with a logging mechanismn. Use LogStep at the start of your sequence to write a trace message into the logger. Additionally, the methods SetBusy and ErrorStack are overloaded to implemented traceability with a logger.

This implementation of (ISequence)[ZCore.ISequence] is the favourable implementation for sequences that do not require alarming mechanism. Most commonly this implementation is utilized in equipment objects.

FUNCTION_BLOCK Sequence EXTENDS ZCore.Sequence IMPLEMENTS ZCore.ISequence, ZCore.IObject, ZCore.IError, ZCore.ICancellationToken, ZCore.IHaltable, ZCore.IStartToken, ZCore.IHaltToken, ZCore.IRecoverable

Constructor

FB_init

Standard method to run the construction code of this function block it simply sets the object state of the state machine to idle because in most cases there is no booting phase necessary

METHOD FB_init (
 bInitRetains : BOOL,
 bInCopyCode : BOOL) : BOOL

Inputs

bInitRetains BOOL

if TRUE, the retain variables are initialized (warm start / cold start)

bInCopyCode BOOL

if TRUE, the instance afterwards gets moved into the copy code (online change)

Returns

BOOL

Methods

AbortErrorId

This method should be called in an actual implementation of a framework object if an error occcured during executing a sequence and was caused by itself. Compared to method Abort here it is possible to set a specific ErrorId for an error to differentiate between different error causes inside an application or sequence without the need of evaluating the error-strings.

The method changes the state of the object to Error or BootingError depending on the previous state of the object Busy or Booting, respectively.

Additionally, it sets a message to indicate the problem. The error source of the issue is set to THIS^ object and the error code is set to 0. Preexisting errors are not overwritten by calling this method twice or more.

Calling this method also resets a stored execution token from a former "Async" call as the token is not relevant any more.

A sequence also writes the error stack to the diagnostics if it is aborted

METHOD PUBLIC AbortErrorId (
 errorId : UDINT,
 message : ZCore.ZString)

Inputs

errorId UDINT

(optional) error code of the issue

message ZString

Assert

This method should be called in an actual implementation of a framework object if an error occcured during executing a sequence and was caused by itself. Compared to method Abort here it is possible to set a specific ErrorId for an error to differentiate between different error causes inside an application or sequence without the need of evaluating the error-strings.

The method changes the state of the object to Error or BootingError depending on the previous state of the object Busy or Booting, respectively.

Additionally, it sets a message to indicate the problem. The error source of the issue is set to THIS^ object and the error code is set to 0. Preexisting errors are not overwritten by calling this method twice or more.

Calling this method also resets a stored execution token from a former "Async" call as the token is not relevant any more.

The sequence also also writes the error stack to the diagnostics if the assertion fails

METHOD PUBLIC Assert (
 obj : ZCore.IError) : BOOL

Inputs

obj IError

Returns

BOOL

BeginQuery

Returns a query object, which can be used to test objects for there current state and react accordingly in a "fluent way". For more documentation on this object see (ISequenceQuery)[xref:ZAux.ISequenceQuery].

METHOD BeginQuery () : ISequenceQuery

Returns

ISequenceQuery

LogStep

This method writes a trace message to the logger, which is utilized by this object, if the step changed from the previously call to this method. This method only works correcly, if the step interface that is utilized by the sequence has properly applied to the sequence with a call to OnStart. See the prototype of the function body of a sequence here and notice the call to IF OnStart(_step).

Note

This implementation utilizes the StepDecoded method, which by default only returns integers that are corresponding to _step.CurrentIndex. Refer to the documentation of StepDecoded if strings should be written into the log.

METHOD PROTECTED LogStep ()

SetBusy

Extends ZCore.Sequence.SetBusy with logging capability. If a logger is configured for this sequence, a trace message is written into the log whenever this method is called.

Calling this method with on=TRUE if the object is in an inactive state (Idle, Error, BootingError) resets diagnostics

METHOD PROTECTED SetBusy (
 busy : BOOL)

Inputs

busy BOOL

SetLogger

Setting a Logger with this method enables the logging functionality of a sequence function block.

Note

To change the logging behaviour it is sometimes useful to inject a LoggerDecorator here. For more information on that pattern read the following documentation (TODO: documentation of the decorator Pattern)

Leaving this Sequence instance without logger will not create any log entries

METHOD SetLogger (
 logger : ZCore.ILogger)

Inputs

logger ILogger

Zeugwerk compatible Logger which has the ILogger interface implemented

Stop

This method stops a running sequence immediately.

This method is overloaded to get a clear log message

METHOD Stop ()

TraceErrorStack

This method is used internally when recording an error trace.

METHOD TraceErrorStack (
 trace : ZCore.IErrorTrace)

Inputs

trace IErrorTrace