Finite State Machine development
States execution
Pysmlib handles all the logic to implement the execution of finite state machine
states. The user only has to implement the actual states, as methods of
fsmBase. Each state can have up to 3 methods defined, for example for a
state called “exampleState” these are:
exampleState_entry(self, *args, **kwargs)[optional]This method is executed only once on the transition from previous state to the current one (“exampleState”). It can be useful for initializations or to perform specific actions related to the transition. For example if this is an error state, one could use the entry part of the error state to perform security actions (like power off the output of a power supply), and then wait on the
evalmethod for a manual reset of the error before continuing. If it is omitted theevalmethod is executed directly.exampleState_eval(self, *args, **kwargs)[mandatory]This the the main body of the state, and the only mandatory part. If this method is defined, so is the state. If this is the current state,it is executed every time an event occurs on one of the FSM inputs. Here the code should check some conditions and when they are met, perform actions accordingly. These can be a
put()to write a value to an output or a change of FSM state, by callinggotoState("nextStateName"). The FSM will remain in this state and execute this method until the first call togotoState().exampleState_exit(self, *args, **kwargs)[optional]This method is the opposite of the
entryone and is execute only on the transition from this state to the next one, with no distinction on the destination. It can be used to perform some clean-up after the execution of the state and to perform actions related to this transition.
This architecture gives easy access to the first and last execution of the
state, which is often useful! Note that after the entry method the library
does not wait for an event to execute the eval one, but it is executed right
away. The same is true for the execution of the exit method after the
eval. This is useful to perform some actions right after the transition to
the next state, without waiting for an event.
These state methods can accept any number of arguments, which are passed to
them by the gotoState() method. This is useful to pass some parameters to
the state, which can be used to perform some actions. For example, if the state
is called “move” and it is used to move a motor, the gotoState('move', 200) method can be called with the number of steps to move as argument, and the move_entry(self, steps) method can use this value to perform the movement. Keyword arguments are also supported. In the same example, one could define the method move_entry(self, steps, speed=100) with the default speed and call gotoState('move', 200, speed=200) to move the motor at a different speed. The entry, eval and exit method will receive the same arguments during their execution.
State definition example
In this example we will see how to program a FSM state will all the three methods available.
The goal of this snippet of code is to achieve a motor movement and wait for its completion before continuing to the next state. Some of the code functionality are explained on the next pages of this documentation.
#######################################################################
# MOVE state
# Entry method: executed only the first time
def move_entry(self):
steps = self.smallStep.val() # get steps to move from a PV
self.logI("Moving %d steps..." % steps) # write to info log
self.motor.put(steps) # motor record PV - this will move the motor
self.tmrSet('moveTimeout', 10) # Set a timer of 10s
# Eval method: executed for each event until gotoState() is called
def move_eval(self):
if self.doneMoving.rising(): # If the motor movement completed
self.gotoState("nextState") # continue to next state
elif self.tmrExpired("moveTimeout"): # Timer expired event
self.gotoState("error") # go to an error state
self.logE("The movement did not complete before timeout reached") #write to error log
# Exit method: executed only the last time
def move_exit(self):
self.logD("Motor status word is: %d" % self.motorStatus.val()) # write to debug log
#######################################################################
Event types
The events which trigger the execution of the current state are:
- Connection events
One of the input has connected or disconnected.
- Change events
One of the inputs has changed value.
- Put complete events
When a call to
put()is executed the value has to be written over the network to the PV. This may take some time and after that the put complete event is notified. When executing aput()on some kinds of PVs, these are executed. The event is returned when the execution has completed.- Timer expired events
These events are local of pysmlib and are used to notify the current state that a previously set timer has reached its maximum time.
There are only two situations where a new state is executed without being triggered by an event:
The first state is evaluated once at startup.
When a transition from a state to the next one occurs, the next one is evaluated once right after the previous one, without waiting for an event.
In these cases, all the methods on the inputs which detect edges (Methods to detect edges) return false.
fsmBase class reference
- class fsmBase(name[, tmgr=None[, ios=None[, logger=None]]])
Create an empty FSM: usually you derive from this to add custom states.
- Parameters:
The optional arguments let you pass shared objects. When they are omitted, they are automatically created by
fsmBasefrom default classes, while derivate ones can be passed. Usually just one instance of the three classes is shared between all the FSMs on an executable. The Loader and fsm execution automatically takes care of these arguments.- gotoState(stateName, *args, **kwargs)
Force a transition from the current state to “stateName”. First of all the
exitmethod of the current state is executed, then the library will look for the three methods associated to the string “stateName”, as described above, will execute theentryandevalmethod, then wait for an event. When this arrives, thestateName_evalmethod is executed again. The parameters passed to this method are passed to theentry,evalandexitmethods of the new state.- Parameters:
stateName (String) – the name of the next state
args – the positional arguments to pass to the state methods
kwargs – the keyword arguments to pass to the state methods
- gotoPrevState(*args, **kwargs)
Return to the previous state. If no args are passed, the previous args are used.
- Parameters:
args – the positional arguments to pass to the state methods
kwargs – the keyword arguments to pass to the state methods
- fsmname()
Return the FSM name
- Returns:
FSM name.
- logE(msg)
Write to log with ERROR verbosity level = 0.
- Parameters:
msg (string) – the log message
- logW(msg)
Write to log with WARNING verbosity level = 1.
- Parameters:
msg (string) – the log message
- logI(msg)
Write to log with INFO verbosity level = 2.
- Parameters:
msg (string) – the log message
- logD(msg)
Write to log with DEBUG verbosity level = 3.
- Parameters:
msg (string) – the log message
- connect(name[, **args])
- Parameters:
name (string) – the PV name, or the map reference to a PV name.
args – optional arguments to be passed to
fsmIOs.get()
- Returns:
fsmIOobject
The optional arguments can be used by
fsmIOsderivate classes to get further specification on the desired input. See I/O mapping and parametrization.
- start()
Start FSM execution.
- kill()
Stop FSM execution. FSM are derivate of
threading.Threadso they cannot be restarted after a kill, but a new instance must be created. However, a better approach is to use an idle state where the FSM will do nothing, instead of killing it.
- tmrSet(name, timeout[, reset=True])
Create a new timer which will expire in timeout seconds, generating an timer expired event, which will execute the FSM current state (at expiration time).
- Parameters:
name (string) – A unique identifier of this timer. The same timer can be reused more than once recalling the same name.
timeout (float) – The expiration time, starting from the invocation of
tmrSet(). [s]reset (boolean) – If this is
Truethe timer can be re-initialized before expiration. Default =True.
- tmrExpired(name)
This will return
Trueif the timer has expired or does not exist.- Returns:
timer expired condition
- tmrExpiring(name)
This will return
Trueif the timer is expiring in the current event (rising condition). This means that it will returnTrueonly during a single execution of the current state, when the state execution was triggered by the timer expiring.- Returns:
timer expiring condition
- isIoConnected()
This will return
Trueonly when all the FSM inputs are connected, meaning that they have received the first connection event.- Returns:
Trueif all I/Os are connected.
- isIoInitialized()
This will return
Trueonly when all the FSM inputs are initialized, meaning that they have received the first value.- Returns:
Trueif all I/Os are initialized.
- setWatchdogInput(input[, mode="on-off"[, interval=1]])
This set an input to be used for the Watchdog logic.
- Parameters:
input (
fsmIOobject.) – the input to use as watchdog.mode (string) – One of “on-off”, “off”, “on”.
interval (float) – the watchdog period [s].
- Raises:
ValueError: Unrecognized input type or mode.
- getWatchdogInput()
Returns the input set as a watchdog or
None.- Returns:
watchdog input or
None.