J
Jay Mackey
ST no good for PLC? We use it almost exclusively (mainly because it is is the best implemented language/dev-environment in Beckhoff/Codesys. I could see using ladder and SFC a LOT more if each had a better dev environment in Codesys.)
What's wrong with this...
PROGRAM XXX_SEQ_TEMPLATE_XXX
VAR_INPUT
Enable :BOOL;
END_VAR
VAR_OUTPUT
Done :BOOL;
Busy :BOOL;
Error :BOOL;
ErrorID :STRING[80];
END_VAR
VAR CONSTANT
(*TODO: Replace this with the name of this POU*)
myName :STRING[32]:='SEQ_TEMPLATE';
END_VAR
VAR
myState :INT;
myLastState :INT;
isDone :BOOL;
ErrorState :INT;
StateTransition :R_TRIG;
Trigger :R_TRIG;
myTimer :TON;
(*Additional local variables here*)
END_VAR
CASE myState OF
0: (*initial state*)
Busy := FALSE;
Done := isDone;
Trigger(clk := Enable);
IF Trigger.Q THEN (* On rising enable line, init sequence... *)
Busy := TRUE;
Done := FALSE;
isDone := FALSE;
Error := FALSE;
ErrorID := ERR_NONE;
ErrorState := 0;
myTimer(in:= FALSE);
(*Add initialization for other variables and sequences here*)
myState := 10;
END_IF;
10: (* Step 1 of sequence *)
(* Wait for initial input state... *)
IF input1
AND input2
AND NOT input3
AND NOT input4
AND NOT (somethingElse OR thatEither)
THEN
myState := 20;
ELSIF WDErr THEN
(* some watchdog process timeout or something... *)
ErrorID := ERR_WDTIMEOUT;
END_IF;
20: (* Step 2 of sequence *)
(* Activate the thingamajig... *)
output1 := TRUE;
StartWDTimer2 := TRUE;
myState := 30;
30: (* Step 3 of sequence *)
(* Wait for feedback of activation of previous thingamajigger... *)
If input 6 AND NOT input7 THEN
myState := 99;
ELSIF WDErr2 THEN
(* another watchdog timer for the activation of the thingamajigger... *)
ErrorID := ERR_WDTIMEOUT2;
END_IF;
99: (* Done *)
isDone := TRUE;
myState := 0;
ELSE
Error := TRUE;
ErrorID := ERR_INVALID_STATE;
LOG_MSG_INT_INT('INVALID STATE TRANSITION IN %D, %D->%D ', myName, myLastState, myState);
END_CASE;
(*Watch for errors to occur*)
IF myState > 0 THEN
IF ErrorID <> '' THEN
Error:= TRUE;
END_IF;
IF Error THEN
LOG_ERROR_STATE(MYNAME, MYSTATE);
ERR_ADD_TRACEBACK(myName, myState);
ErrorState := myState;
myState := 0;
END_IF;
END_IF
(*Watch for state transitions*)
IF myState <> myLastState THEN
LOG_TRANSITION(myName, myLastState, myState);
END_IF;
myLastState := myState;
Each case statement should be coded like a rung of a ladder, but 'could' have some math or functions or whatever in them, as long as the function isn't blocking or processor-greedy. If another FB of indeterminate duration needs to be kicked off from one of the above case 'states', then the enable input of that FB is merely set to high from that case statement. Any given case then executes quickly, like a rung of a ladder, evaluating to some resolution (or none, meaning on the next scan of the PLC, that same case is evaluated again (and again) to see if a real-world sensor-state has come true, or a user watchdog process has come true instead).
When done without error, this program (or FB) returns to state 0 with it's Done flag set and Busy flag off. State 0 continues executing indefinitely until the enable input is set low and then high again, which resets the Done flag, reinits, and restarts the sequence.
The above is my boilerplate template for a ST state machine.
Jay
What's wrong with this...
PROGRAM XXX_SEQ_TEMPLATE_XXX
VAR_INPUT
Enable :BOOL;
END_VAR
VAR_OUTPUT
Done :BOOL;
Busy :BOOL;
Error :BOOL;
ErrorID :STRING[80];
END_VAR
VAR CONSTANT
(*TODO: Replace this with the name of this POU*)
myName :STRING[32]:='SEQ_TEMPLATE';
END_VAR
VAR
myState :INT;
myLastState :INT;
isDone :BOOL;
ErrorState :INT;
StateTransition :R_TRIG;
Trigger :R_TRIG;
myTimer :TON;
(*Additional local variables here*)
END_VAR
CASE myState OF
0: (*initial state*)
Busy := FALSE;
Done := isDone;
Trigger(clk := Enable);
IF Trigger.Q THEN (* On rising enable line, init sequence... *)
Busy := TRUE;
Done := FALSE;
isDone := FALSE;
Error := FALSE;
ErrorID := ERR_NONE;
ErrorState := 0;
myTimer(in:= FALSE);
(*Add initialization for other variables and sequences here*)
myState := 10;
END_IF;
10: (* Step 1 of sequence *)
(* Wait for initial input state... *)
IF input1
AND input2
AND NOT input3
AND NOT input4
AND NOT (somethingElse OR thatEither)
THEN
myState := 20;
ELSIF WDErr THEN
(* some watchdog process timeout or something... *)
ErrorID := ERR_WDTIMEOUT;
END_IF;
20: (* Step 2 of sequence *)
(* Activate the thingamajig... *)
output1 := TRUE;
StartWDTimer2 := TRUE;
myState := 30;
30: (* Step 3 of sequence *)
(* Wait for feedback of activation of previous thingamajigger... *)
If input 6 AND NOT input7 THEN
myState := 99;
ELSIF WDErr2 THEN
(* another watchdog timer for the activation of the thingamajigger... *)
ErrorID := ERR_WDTIMEOUT2;
END_IF;
99: (* Done *)
isDone := TRUE;
myState := 0;
ELSE
Error := TRUE;
ErrorID := ERR_INVALID_STATE;
LOG_MSG_INT_INT('INVALID STATE TRANSITION IN %D, %D->%D ', myName, myLastState, myState);
END_CASE;
(*Watch for errors to occur*)
IF myState > 0 THEN
IF ErrorID <> '' THEN
Error:= TRUE;
END_IF;
IF Error THEN
LOG_ERROR_STATE(MYNAME, MYSTATE);
ERR_ADD_TRACEBACK(myName, myState);
ErrorState := myState;
myState := 0;
END_IF;
END_IF
(*Watch for state transitions*)
IF myState <> myLastState THEN
LOG_TRANSITION(myName, myLastState, myState);
END_IF;
myLastState := myState;
Each case statement should be coded like a rung of a ladder, but 'could' have some math or functions or whatever in them, as long as the function isn't blocking or processor-greedy. If another FB of indeterminate duration needs to be kicked off from one of the above case 'states', then the enable input of that FB is merely set to high from that case statement. Any given case then executes quickly, like a rung of a ladder, evaluating to some resolution (or none, meaning on the next scan of the PLC, that same case is evaluated again (and again) to see if a real-world sensor-state has come true, or a user watchdog process has come true instead).
When done without error, this program (or FB) returns to state 0 with it's Done flag set and Busy flag off. State 0 continues executing indefinitely until the enable input is set low and then high again, which resets the Done flag, reinits, and restarts the sequence.
The above is my boilerplate template for a ST state machine.
Jay