This library provides a small object-oriented state machine framework for SIMATIC AX.
It is designed for simple single-path state machines with exactly one active state at a time.
apax add @simatic-ax/statemachineTo install this package, you need access to the GitHub package registry. More information is available here.
Simatic.Ax.StateFramework
The framework centers around StateController, IState, ITransition, and IGuard.
StateControllermanages one active state.- A state exposes zero or more outgoing transitions.
- A transition becomes active when its guard returns
TRUE. - The first transition whose guard evaluates to
TRUEwins in a cycle.
This framework does not implement parallel states, fork/join semantics, or multiple active states.
The controller executes the state lifecycle in this order:
- If no active state exists yet, the controller activates
InitialState. OnEntry()is called once when a state becomes active.- Transitions are evaluated in index order.
- If no transition is taken,
Action()is executed on the active state. - If a transition is taken, the current state executes
OnExit(), the next state executesOnEntry(), and the next state'sAction()may run in the same cycle.
- If
InitialStateisNULL, the controller entersSTATUS_NO_INITIALSTATE. - If a taken transition has no target state, the controller enters
STATUS_NO_NEXTSTATE. - If the active state has no outgoing transitions, the controller enters
STATUS_NO_TRANSITION. - Error states are latched. Once an error status is active, later
Execute()calls do not recover automatically. Restart()terminates the current state and re-entersInitialState.
States implement IState. The provided AbstractState already handles:
StateIDStateNameStateStatus- one default transition slot via
Transition1
For convenience, the library also provides:
State1TransitionState2TransitionState3Transition
These helper classes support one, two, or three outgoing transitions.
A transition connects:
- one
IGuard - one target
IState
The controller evaluates transitions in order and switches to the first matching target state.
AndGuard returns TRUE only if both child guards exist and both return TRUE.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
timeoutGuard1 : TimeoutGuard := (Timeout := T#1500ms);
countGuard1 : CountGuard := (Count := LINT#5);
guard1 : AndGuard := (Lhs := timeoutGuard1, Rhs := countGuard1);
END_VAR
END_PROGRAM
BoolGuard evaluates a REF_TO BOOL. It returns FALSE if the reference is NULL.
Note: BoolGuard is level-based. Reset() does not change its behavior.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
bValue : BOOL;
guard1 : BoolGuard := (Value := REF(bValue));
END_VAR
END_PROGRAM
CompareGuardLint compares a referenced LINT value with a configured threshold.
Supported conditions in the current implementation:
| Condition | Meaning |
|---|---|
GT |
value > compareToValue |
EQ |
value = compareToValue |
LT |
value < compareToValue |
GE |
value >= compareToValue |
LE |
value <= compareToValue |
Note: Although the Condition enum also contains NE, the current Check() implementation does not handle it explicitly.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
value : LINT;
guard1 : CompareGuardLint := (
Value := REF(value),
CompareToValue := LINT#500,
Condition := Condition#GT
);
END_VAR
END_PROGRAM
CountGuard increments an internal counter on every Check() call. When the counter reaches Count, it returns TRUE once and resets its internal counter to zero.
Note: Reset() is currently a no-op. The guard keeps counting across cycles until it fires.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
guard1 : CountGuard := (Count := LINT#5);
END_VAR
guard1.Config(countValue := LINT#5);
END_PROGRAM
NotGuard negates the result of its child guard. If the child guard is NULL, it returns FALSE.
OrGuard returns TRUE if at least one configured child guard returns TRUE.
If one side is NULL, the other side is evaluated on its own.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
timeoutGuard1 : TimeoutGuard := (Timeout := T#1500ms);
countGuard1 : CountGuard := (Count := LINT#5);
guard1 : OrGuard := (Lhs := timeoutGuard1, Rhs := countGuard1);
END_VAR
END_PROGRAM
TimeoutGuard uses System.Timer.OnDelay. It returns TRUE after the configured timeout has elapsed.
Reset() drops the timer input so the timeout starts again on the next Check() call.
TrueGuard always returns TRUE.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
guard1 : TrueGuard;
END_VAR
END_PROGRAM
XorGuard returns TRUE if exactly one configured child guard returns TRUE.
If one side is NULL, the other side is evaluated on its own.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
timeoutGuard1 : TimeoutGuard := (Timeout := T#1500ms);
countGuard1 : CountGuard := (Count := LINT#5);
guard1 : XorGuard := (Lhs := timeoutGuard1, Rhs := countGuard1);
END_VAR
END_PROGRAM
StateLogger is a simple ring buffer for log messages.
- It stores up to 100 entries in
MsgBuffer. - New entries overwrite old entries in a circular manner.
- The current controller implementation exposes
Logger : ILogger, but it does not actively emit log messages yet.
USING Simatic.Ax.StateFramework;
PROGRAM SampleProgram
VAR
controller : StateController;
startState : MyStartState := (StateID := 1, StateName := 'Start');
nextState : MyNextState := (StateID := 2, StateName := 'Next');
transition1 : Transition;
guard1 : BoolGuard;
switchState : BOOL;
END_VAR
guard1.Value := REF(switchState);
transition1.Guard := guard1;
transition1.NextState := nextState;
startState.Transition1 := transition1;
controller.InitialState := startState;
controller.Execute();
END_PROGRAM
CLASS MyStartState EXTENDS AbstractState
METHOD PUBLIC OVERRIDE OnExit
END_METHOD
METHOD PUBLIC OVERRIDE Action
END_METHOD
END_CLASS
CLASS MyNextState EXTENDS AbstractState
METHOD PUBLIC OVERRIDE OnExit
END_METHOD
METHOD PUBLIC OVERRIDE Action
END_METHOD
END_CLASS
Thanks for your interest in contributing. Please use issues for bugs, unclear documentation, or feature requests, and open a merge request for proposed changes.
Please read LICENSE.md.




