Finite state machines (FSB) are a great tool for modeling equipment and machine functionality. It provides user with tools to model behavior of equipment, well defined programming interface and ability to easily monitor and trace machine operation. Produced code is simple, clear and easy to maintain. Engineering effort is moved from coding to modeling behavior of equipment using state charts. A simple valve control will be used to demonstrate this approach.
To start we will use FSB to model a valve (hydraulic/pneumatic). Valve have one spool which, when energized, will extend cylinder to its final position. When deenergized cylinder will retract to initial position. One DO is used for energizing spool and two DI for monitoring initial and final position. Additionally three more signals are used:
- Emergency stop
- Equipment ready – Signal indicating it is safe to move without possible collision with another equipment
- MCCOK – Usually feedback from valve power supply
Functionality is simple: When deenergized valve is in its initial state and cylinder is retracted to initial position. Initial position is indicated by limit switch (EndPosOFF). On rising edge of CmdON (^CmdON) valve will be energized and cylinder will travel for some finite time to its end position. End position is indicated by end limit switch (EndPosON). Command CmdOFF will deenergize the valve and cylinder will travel to its initial position. While CmdOFF is active it is not possible to energize the valve. New command will be active again on ^CmdON. This functionality is represented on following diagram.
Since we will use this type of diagram in other posts little explanation is required:
- Ovals represent states. Each state is identified by name and number (OFF 16#0001). Each FSB start in initial state (Init 16#0000). Rising edge of Enable signal is required to start FSB functionality. Enable must be TRUE at all times, otherwise FSB will move to Init state and finctionality fill be disabled.
- Transitions from one to next state are represented by arrows. Direction of an arrow indicates next state. Small bullet on beginning of transition indicates priority of transition. Smaller the number, transition have higher priority. Blue bullet indicate controlled state change, red ones error state changes
- Dashed rectangle with bullets represent group of states with state change activated by common signal. This signal will move FSB to common state regardless of current state of FSB.
Presented diagram shows only transitions that represent control functionality of FSB. Typically control diagram must be accompanied by error transition diagram .
Since error handling represent large portion of the code, dividing functionality like this gives programmer possibility to focus on control or error handling logic separately. Of course these two diagrams can be combined to one, but it is my opinion that this approach may have its benefits.
Generally, there are three types of states. FSB will be in initial state as long as Enable signal is FALSE. In initial state FSB functionality is disabled. Resident state is a state in which FSB can stay for indefinite period of time (in our case ON, OFF, Aborted). Transition out of this state is only exterior event based (command or feedback). Transition states are ones in which FSB remains for finite time and indicate transition (physical or logical) form one to another resident state (in our case WaitON, WaitOFF and Aborting). Transition states, in general should be guarded with timers (in our case DelayON, DelayOFF). If the timer expires, automatic transition from this state must be triggered. As a rule of thumb, transition from one to another resident state is possible only through transition state. Direct transition from one to another resident state should be avoided. For this reason Aborting state is inserted in front of Aborted state. Automatic transition to Aborted takes place when system reaches EndPosOFF.
Valve functionality is realized in one Function Block (FB) in Siemens TIA Portal. Typical use is shown on next picture.
This is setup for simulating commands, parameters, feedback signals from limit machine and control cabinet. Function block outputs represent full set of signals which can be used for control logic and troubleshooting problems.
Structured text code for valve control is rather simple. FSM code is in form of one CASE instruction. Control transitions only move FSM from one to another state. Error transitions, in addition, set error code consisting of cause of error and state in which error is set. Outputs are generated by validating current operating state of the valve. No complicated logic, no SET/RESET. Fundamental difference form using “standard” logic is that when using FSM we search and code transitions in valve operation. in “standard” logic we look for states and code logic which will keep valve in one of these states. To code functionality like this in “standard” logic would require much more effort, code and work.
Another difference is: coding FSM is simple. Making functional FSM diagram is not. Most of the engineering work is done to produce a FSM diagram which will cover all use cases and all functionality. To give an example: On first diagram, transition from WaitOFF to WaitON state on repeated CmdON is missing. This is deliberate design decision. In this way we prevent repeated on command until cylinder reaches EndPosOFF. This may or may not be desired behavior of the device and must be taken into account during design. Adding or removing this functionality is trivial using FSM. Adding this functionality uslig “standard” logic may be very difficult. One more example: Watchdog timers only move FSM from transient states (WaitON, WaitOFF) to resident states (ON, OFF). In transient states we wait for end limits and when they are reached we move on. If not an error should be raised. We could do it in transient states but then we would duplicate the code since same check must be made in resident states. This approach of only moving to another state when watchdog expires makes simulation of system very easy, as shown on previous picture.
If FSM are used throughout complete PLC program to model functionality of the system from basic elements like sensors and actuators, to complete machine logic, we get system that is well structured, easily modified and easy for an end user. I will try to demonstrate this in posts that will follow.
FUNCTION_BLOCK "ValveSingle" { S7_Optimized_Access := 'FALSE' } VERSION : 0.1 ////============================================================================= //// Dragan Grujicic //// code4plc.org //// (c)Copyright (2018) All Rights Reserved ////----------------------------------------------------------------------------- //// //// Single valve functionality using finite state machines (FSM) //// //// //// //// ////----------------------------------------------------------------------------- //// Change log table: //// Version Date Expert in charge Changes applied //// 01.00.00 dd.mm.yyyy (Name of expert) First released version ////============================================================================= VAR_INPUT Enable : Bool; // Enable valve control CmdON : Bool; // Switch valve ON. Active on TRUE CmdOFF : Bool; // Switch valve OFF. Active on TRUE (FALSE for normal operation) EmergencyStop : Bool; // Emergency stop of the valve MCCOK : Bool; // Power supply OK EquipmentReady : Bool := true; // Mechanical equipment ready. Must be TRUE all time valve is in operation EndPositionON : Bool; // End limit switch in ON position EndPositionOFF : Bool; // End limit switch in OFF position TimeDlyON : Time := T#1s; // [s] Time required to reach end position in ON direction TimeDlyOFF : Time := T#1s; // [s] Time required to reach end position in OFF direction Reset : Bool; // Reset control errors END_VAR VAR_OUTPUT Valid : Bool; // Control signals valid: enable and no error ON : Bool; // Control signal for the valve: ON untill #CmdOFF ONlimited : Bool; // Control signal for the valve: ON until #EndPositionON or #CmdOFF ReadyRun : Bool; // Valve ready run: Ready to follow command Busy : Bool; // Valve control busy: Valve in state other than OFF ActiveState : UInt; // Current valve state AtPositionON : Bool; // End limit switch in ON position AtPositionOFF : Bool; // End limit switch in OFF position Error : Bool; // Valve error active Status : UInt; // Status signal indicating current valve state END_VAR VAR iActiveState : UInt; // Current valve state iNextState : UInt; // Next valve state iValid : Bool; // Valve control processing valid iError : Bool; // Valve error active iErrorID : UInt; // Error ID TON_DlyON {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME; TON_DlyOFF {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME; R_TRIG_CmdON {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG; R_TRIG_Reset {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG; R_TRIG_Enable {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG; END_VAR VAR CONSTANT STATE_INITIAL : UInt := 16#0000; // Initial state STATE_OFF : UInt := 16#0001; // Valve OFF STATE_WAIT_ON : UInt := 16#0002; // Valve is ON and moving to end limit ON STATE_ON : UInt := 16#0003; // Valve is ON STATE_WAIT_OFF : UInt := 16#0004; // Valve is OFF and moving to end limit OFF STATE_ABORTING : UInt := 16#0005; // ERROR detected and motor is switched OFF: Waiting to stop STATE_ABORTED : UInt := 16#0006; // ERROR detected and motor is switched OFF: Waiting for reset ERROR_UNKNOWN_STATE : UInt := 16#8010; // Unknown current state error ERROR_MCC_NOT_VALID : UInt := 16#8020; // Power supply for the valve not OK ERROR_EQUIPMENT_NOT_READY : UInt := 16#8030; // Mechanical equipment of the valve not ready ERROR_EMERGENCY_STOP : UInt := 16#8040; // Emergency stop active ERROR_END_POS_ON : UInt := 16#8050; // Equipment in ON position with valve OFF ERROR_END_POS_OFF : UInt := 16#8060; // Equipment in OFF position with valve ON END_VAR BEGIN //*********************************************************************************************// //*********************** Valve State Transition Detection ************************************// #R_TRIG_Enable(CLK:=#Enable); #R_TRIG_CmdON(CLK:=#CmdON); #R_TRIG_Reset(CLK:=#Reset); //*********************************************************************************************// //******************************* Timer Evaluation ********************************************// #TON_DlyON(IN:=#iActiveState = #STATE_WAIT_ON, PT:=#TimeDlyON); #TON_DlyOFF(IN:=#iActiveState = #STATE_WAIT_OFF, PT:=#TimeDlyOFF); //*********************************************************************************************// //*************************** Valve State Transitions *****************************************// IF #Enable THEN //Check to see if #EmergencyStop, #MCCOK and #EquipmentReady are OK //If not automaticaly go to #STATE_ABORTING, but only if there is not active error on valve //These signals need to be TRUE all the time. //Check this frist so we do not need to check it in state transitions IF NOT #iError AND NOT #EmergencyStop THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_EMERGENCY_STOP OR #iActiveState; ELSIF NOT #iError AND NOT #MCCOK THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_MCC_NOT_VALID OR #iActiveState; ELSIF NOT #iError AND NOT #EquipmentReady THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_EQUIPMENT_NOT_READY OR #iActiveState; ELSE //Valve state transitions CASE #iActiveState OF 0: //Initial state: Move to #STATE_OFF only on #Enable IF #R_TRIG_Enable.Q THEN #iNextState := #STATE_OFF; END_IF; #STATE_OFF: //OFF state: Move to #STATE_WAIT_ON on rising edge of #CmdON. //#CmdOFF must be FALSE at all times for transition to take place //If #EndPositionOFF is lost or system is in #EndPositionON raise #Error IF NOT #CmdOFF AND #R_TRIG_CmdON.Q THEN #iNextState := #STATE_WAIT_ON; ELSIF #EndPositionON THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_END_POS_ON OR #iActiveState; ELSIF NOT #EndPositionOFF THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_END_POS_OFF OR #iActiveState; END_IF; #STATE_WAIT_ON: //Valve active and system is traveling to #EndPositionON. //IF #CmdOFF is active at any moment we shut off valve without //possibility to recover until #STATE_OFF is reached //If watchdog time elepses before #EndPositionON is reached move to next state //We do not raise error here but wait for ON state IF #CmdOFF THEN #iNextState := #STATE_WAIT_OFF; ELSIF #EndPositionON OR #TON_DlyON.Q THEN #iNextState := #STATE_ON; END_IF; #STATE_ON: //Valve is active AND system is at #EndPositionON. //If #CmdOFF is active at any moment we shut off valve //without possibility to recover until #STATE_OFF is reached //If #EndPositionON is lost or system is in #EndPositionOFF raise #Error IF #CmdOFF THEN #iNextState := #STATE_WAIT_OFF; ELSIF #EndPositionOFF THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_END_POS_OFF OR #iActiveState; ELSIF NOT #EndPositionON THEN #iNextState := #STATE_ABORTING; #iError := TRUE; #iErrorID := #ERROR_END_POS_ON OR #iActiveState; END_IF; #STATE_WAIT_OFF: //Valve is not active and system is traveling to #EndPositionOFF. //Wait for #EndPositionOFF or #TimeDelayOFF before moving TO #STATE_OFF *) IF #EndPositionOFF OR #TON_DlyOFF.Q THEN #iNextState := #STATE_OFF; END_IF; #STATE_ABORTING: IF #EndPositionOFF THEN #iNextState := #STATE_ABORTED; END_IF; #STATE_ABORTED: IF #EndPositionOFF AND #R_TRIG_Reset.Q THEN #iNextState := #STATE_OFF; #iError := FALSE; #iErrorID := 0; END_IF; ELSE //If current state is out of 0 TO #MAX_STATES go to state 0 and raise error // !!!!! DO NOT MODIFY !!!!! // #iNextState := 0; #iError := TRUE; #iErrorID := #ERROR_UNKNOWN_STATE; END_CASE; END_IF; ELSE #iNextState := 0; #iError := FALSE; END_IF; //*********************************************************************************************// //************************************ Change State *******************************************// #iActiveState := #iNextState; #iValid := #Enable; //**********************************************************************************************// //********************************** Set Outputs ***********************************************// #Valid := #iValid; #ON := #iValid AND ((#iActiveState = #STATE_WAIT_ON) OR (#iActiveState = #STATE_ON)); #ONlimited := #iValid AND (#iActiveState = #STATE_WAIT_ON); #Busy := #iValid AND (#iActiveState #STATE_OFF AND #iActiveState 0); #ReadyRun := #iValid AND NOT #iError; #ActiveState := #iActiveState; IF #iError THEN #Status := #iErrorID; ELSE #Status := #iActiveState; END_IF; #AtPositionON := #iValid AND #EndPositionON; #AtPositionOFF := #iValid AND #EndPositionOFF; #Error := #iValid AND #iError; END_FUNCTION_BLOCK