Moddy Finite State Machines

Finite State Machine

class moddy.fsm.Fsm(dict_transitions, parent_fsm=None)[source]

A finite state machine. Subclass your FSM from this class.

Example:

class Computer(Fsm):

    def __init__(self):

        transitions = {
            '':
                [('INITIAL', 'off')],
            'off':
                [('PowerApplied', 'standby')],
            'standby':
                [('PowerButtonPressed', 'normal_op')],
            'normal_op':
                [('PowerButtonPressed', 'standby'),
                 ('OsShutdown', 'standby')],
            'any':
                [('PowerRemoved', 'off')]
        }

        super().__init__( dictTransitions=transitions )

The special ANY state means that the transitions can be initiated from ANY state. The special INITIAL event must be in the ‘’ (uninitialized) state and specifies the INITIAL transistion which is triggered by start_fsm().

You can define entry and exit method that are executed when a state is entered or left. These methods must follow the naming convention state_<statename>_<entry/exit> They don’t need to exist. They are called only if they are defined.

Note that entry and exit actions are NOT called at self transitions (transitions to the current state):

# Off actions
def state_off_entry(self):
    print("state_off_entry")

def state_off_exit(self):
    print("state_off_exit")

You can also define a “do” Method that is invoked

  • after the “Entry” methode

  • at self transistions to the state

These methods must follow the naming convention state_<statename>_do

Such routines can be also defined for the special ANY state. If they exist they are called at the entry or exit or self transitions to/from any state.

Use the fsm as follows:

comp = Computer()
comp.start_fsm()    # sets the state machine to its initial state

comp.event('PowerApplied')
print("State %s" % comp.state)

comp.event('PowerButtonPressed')
print("State %s" % comp.state)

comp.event('PowerRemoved')
print("State %s" % comp.state)

You can call exec_state_dependent_method() to execute a state specific method of the fsm. e.g. exec_state_dependent_method('msg', 123) calls state_<currentStateName>_msg( 123 )

(e.g. the simFsmPart uses it to execute the _msg and _expiration functions)

Hierarchically Nested State Support

https://en.wikipedia.org/wiki/UML_state_machine#Hierarchically_nested_states

Rules: Nested states are defined by the user in the transition list:

Main FSM:

transitions = {
    '':
        [('INITIAL', 'off')],
    'off':
        [('PowerApplied', 'standby')],
    'standby':
        [('PowerButtonPressed', 'normal_op')],
    'normal_op':
        [
        ####### NESTED FSM ('fsm-Name', Class-Name)
         ('fsm-name' , subfsm),
         ('PowerButtonPressed', 'standby'),
         ('OsShutdown', 'standby')],
    'any':
        [('PowerRemoved', 'off')]
}
  • A nested FSM is instantiated when the upper level state is entered

  • A nested FSM cannot exit

  • A nested FSM receives all events from the upper level FSM. If the event is not known in the nested FSM, it is directed to the upper FSM. Events that are known in the nested FSM are NOT directed to upper FSM

  • If the upper state exits, the exit action of the current states (first, the state in the nested fsm, then the upper fsm) are called. Then the nested fsm is terminated.

  • Orthogonal nested states are also supported. Meaning, multiple nested fsms exist in parallel. Just enter multiple subFsms in the transition list of a state.

  • For nested statemachines, the following methods are usefull:

    • top_fsm() gives you the reference to the top level Fsm. E.g. to fire an event to the top Fsm.

    • moddy_part() gives you the moddy part where the state machine is contained, regardless of the fsm nesting level

Parameters
  • dict_transitions (dict) – a dictionary, with the transitions: The dict key is the state, and the values are a list of transition from that state. Each transition consists of a tuple (event, targetState).

  • parent_fsm (Fsm) – The parent Finite State Machine. None if no parent.

event(ev_name)[source]

Execute an Event in the ANY and current state.

Parameters

ev_name – event to execute

Raises

AssertionError – if the current state is None.

Returns

True if the event causes a state change, False if not.

exec_state_dependent_method(method_name, deep, *args, **kwargs)[source]

Execute the state specific methods:

The method self.state_any_<method_name>(*args,**kwargs) is called if it exists.

The method self.state_<stateName>_<method_name>(*args,**kwargs) is called if it exists.

Parameters
  • method_name – method name to call

  • deep – if True, then for each currently active sub_fsm, the _exec_state_method is called

Returns

True if at least one method exists

has_event(ev_name)[source]

check if the event is known by the fsm or a currently active statemachine.

Returns

True if event is known by the fsm or a currently active statemachine

moddy_part()[source]

return a reference of the moddy part this fsm is contained in (regardless of the fsm nesting level). return None if it is not included in a moddy part

set_state_change_callback(callback)[source]

Register a method that is called whenever the state of the fsm changes

Parameters

callback – function to be called on state changes

start_fsm()[source]

start the FSM. Fire event INITIAL

top_fsm()[source]

get a reference to the topmost Fsm in the hierarchy

Moddy Part using a Finite State Machine

class moddy.fsm_part.SimFsmPart(sim, obj_name, fsm, parent_obj=None, status_box_repr_map=None)[source]

A moddy part with a state machine All simulator events are directed to the fsm.

Parameters
  • sim – Simulator instance

  • obj_name – part name

  • fsm (Fsm) – the state machine (Fsm class) object created by caller

  • parent_obj – parent part. None if part has no parent. Defaults to None

  • statusBoxReprMap (dict) –

    defines how each state is shown in sequence diagrams’ status boxes Must be a dictionary with the state names as keys. The values must be a tuple of

    • text to display (None if org. state name shall be used)

    • appearance: The colors of the status box, see setStatIndication()

create_ports(ptype, list_port_names)[source]

Convinience functions to create multiple ports at once.

Parameters
  • ptype – Type of ports, must be one of ‘in’, ‘out’ or ‘io’

  • list_port_names (list) – list of port names to create

The function creates for each port a member variable with this name in the part.

create_timers(list_timer_names)[source]

Convinience functions to create multiple timers at once.

Parameters

list_timer_names (list) – list of timer names to create

The function creates for each port a member variable with this name in the part.