This simple Trafficlight example is solved with a state pattern of the @simatic-ax/statemachine
library. This traffic light shows the the phases of a german traffic light (red
--> red-yellow
--> green
--> yellow
--> red
and so on). Each phase will be active for one second.
stateDiagram-v2
[*] --> red
red --> redyellow : T#1s
redyellow --> green : T#1s
green --> yellow : T#1s
yellow --> red : T#1s
state : "red-yellow" AS redyellow
Each state is realized as a own class which is extended from State1Transition
Example:
CLASS StateRed EXTENDS AbstractTrafficLightState
VAR
count : LINT;
activations : LINT;
END_VAR
VAR PUBLIC
rd : REF_TO BOOL;
END_VAR
METHOD PUBLIC OVERRIDE OnEntry
activations := activations + 1;
rd^ := TRUE;
END_METHOD
METHOD PUBLIC OVERRIDE Action
count := count + 1;
END_METHOD
METHOD PUBLIC OVERRIDE OnExit
rd^ := FALSE;
END_METHOD
METHOD PUBLIC OVERRIDE GetColor : Colors
GetColor := Colors#Red;
END_METHOD
END_CLASS
In this example, the methods OnEntry
, OnExit
and OnAction
of the class StateRed
will be overridden with our own code. For example, When the state will be activated, the OnEntry method will be execute once. That means, the this example, the variable activation
will be incremented by 1 and the variable rd
which is a reference to a boolean variable will be set to true.
For the traffic light application, there are four states necessary:
- StateGreen
- StateRed
- StateRedYellow
- StateYellow
For each state are unit tests available. You'll find them in the test-folder:
These tests can be executed direct in the IDE:
This happens in the TrafficlightWrapper
. This wrapper has internally the instances of all states. Additionally, the each state needs a Transition of type ITransition
- in this example jsut of the Type Transition
which has implemented the interface ITransition
.
The Transition is responsible, to switch from the one state to the next state. That means, the transition need to know, the NextState
of type IState
and the condition of type IGuard
, when the transition is fulfilled. In this case, the Check()
method of a Guard
returns true
.
In our example we use a TimeoutGuard
wich returns true
after a configured time is elapsed.
In Our example, you'll find the instances of the states, transitions and the TimeoutGuard.
VAR
_stateRed : StateRed;
_stateRedTrans : Transition;
_TimeoutGuard : TimeoutGuard;
_stateRedYellow : StateRedYellow;
_stateRedYellowTrans : Transition;
_stateGreen : StateGreen;
_stateGreenTrans : Transition;
_stateYellow : StateYellow;
_stateYellowTrans : Transition;
init : BOOL;
END_VAR
In order for the state machine to work in the end, we also need a StateController. This only needs the initial state.
VAR
_sc : StateController;
END_VAR
Since it is currently not possible, to use initializers for references and also not possible to use initial values in the TIAX use case, we need to define a init routine in our wrapper which is called once.
IF NOT init THEN
// Congfigure StateController
_sc.InitialState := _stateRed;
_TimeoutGuard.Timeout := T#1000ms;
// Configure Red
_stateRed.Transition1 := _stateRedTrans;
_stateRed.StateID := 1;
_stateRedTrans.NextState := _stateRedYellow;
_stateRedTrans.Guard := _TimeoutGuard;
_stateRed.rd := REF(rd);
// ...
init := TRUE;
END_IF;
In ths init routine, we parametrize our state machine.
-
Set the initial state to
_stateRed
Example:
_sc.InitialState := _stateRed;
-
Set the
Timeout
parameter for theTimeoutGuard
to 1000ms;Example:
_TimeoutGuard.Timeout := T#1000ms;
-
Configure the Transitions for each state
Example:
_stateRedTrans.NextState := _stateRedYellow; _stateRedTrans.Guard := _TimeoutGuard;
-
Configure all states:
Example for state red:
_stateRed.Transition1 := _stateRedTrans; _stateRed.StateID := 1;
That the state machine runs, the cyclic method Execute()
must be called in the Wrapper.
_sc.Execute();
Now our traffic light is complete and can be imported in a TIA Portal global library --> How to crate a global library