Package: pyeds
Python Event Driven SystemTable of contentsIntroductionInstallationUsing PIPInstalling from sourceCode documentationHow to use itDeclaring a FSM classDeclaring a state classInstantiating the FSMBlinky exampleEventGenerate an eventRules about event namingTimersStateState machineState transitionHierarchical Finite State Machines (HFSM)SourceOther linksIntroductionThis package provides a means to efficiently write finite state machines (FSM)
by hand. The focus was to make the API as simple as possible since no GUI
tools are included to define a FSM.InstallationUsing PIPPyEDS can be installed using the standard Python toolpipwithpip install pyedsInstalling from sourceThe easiest way to install PyEDS from source is to usesetup.pyscript
which uses setuptools. For complete documentation about this script please
refer to setuptools manual.To install from source issue the following command:python setup.py installCode documentationCode documentation is bundled together with the source. The documentation
scripts use Sphinx to generate documents.To generate HTML documentation:cd doc
make htmlThis will create HTML documents indoc/_build/htmldirectory.The documentation can be accessed via Python interpreter, too.>>>importpyeds>>>help(pyeds.fsm)How to use itThe basic routine to create a state machine is the following:Declare a FSM classDeclare all state classesInstantiate FSM classDeclaring a FSM classFSM class is the entry point of a FSM which is used to receive events (see
below) and do the transitions between states. Each FSM must declare it’s own
class which is a subclass ofStateMachine. The simplest way is to just
declare an empty class which inherits the classStateMachine:frompyedsimportfsmclassMyFsm(fsm.StateMachine):passDeclaring a state classEach state is represented by different class. Every method in that class may
handle one particular event. To declare the state, a class must be decorated
withDeclareStatedecorator which require state machine as an argument.
This decorator binds the state class to the specific FSM class. Also, the new
state class must be a subclass ofStateclass:@fsm.DeclareState(MyFsm)classMyState(fsm.State):passDeclare a new class per state.Instantiating the FSMTo instantiate the FSM class do the following:my_fsm=MyFsm()After object initialization the FSM is put into running state.Blinky exampleThe following is an example of FSM which is called Blinky. The FSM will print
‘on’ text and ‘off’ text on console with 0.5 seconds of delay between the
messages.The Blinky FSM has 2 states:State OnState Offo----+
     |
 On  v                Off
+----+----+  blink   +---------+
|         +--------->+         |
|         |          |         |
|         +<---------+         |
+---------+  blink   +---------+The eventblinkis used to trigger transitions between the states.frompyedsimportfsm# The first step is to declare a class which represent custom FSM.classBlinkyFsm(fsm.StateMachine):pass# The second step is to start writing the states of new state machine:@fsm.DeclareState(BlinkyFsm)classInitialization(fsm.State):defon_init(self):fsm.Every(0.5,'blink')returnStateOn@fsm.DeclareState(BlinkyFsm)classStateOn(fsm.State):defon_entry(self):print('on')defon_blink(self,event):returnStateOff@fsm.DeclareState(BlinkyFsm)classStateOff(fsm.State):defon_entry(self):print('off')defon_blink(self,event):returnStateOn# The final step is to instantiate the FSM class defined in the first step.blinky_fsm=BlinkyFsm()After creation the FSM is automatically put into a running state.EventAn event is a notable occurrence at a particular point in time. Events can, but
do not necessarily, cause state transitions from one state to another in state
machines.An event can have associated parameters, allowing the event to convey not only
the occurrence but also quantitative information about the occurrence.An event is the only means of communication between state machines. Each event
carries name. Based on the event name a handler will be called from current
state class which has the same name.An event in PyEDS is instanced using classEvent.The associated parameters with an event are:Name of the event: this is a string containing event name.Owner of event: specifies which state machine has generated this event.Generate an eventTo generate a new event just instantiateEventclass with event name as
parameter:new_event=fsm.Event('my_special_event')Alternative way is to first declare a new event class and instantiate this
derived class:classMySpecialEvent(fsm.Event):passnew_event=MySpecialEvent()# This event is implicitly# called 'my_special_event'In this case baseEventclass will implicitly take the name of the class as
own name. This can be overridden by calling the super constructor:# This event has the exact same name as the above oneclassDerivedEvent(fsm.Event):def__init__(self):super().__init__('my_special_event')Rules about event namingWhen an event is created and sent to a state machine it’s name is used to decide
which method in current state instance should be invoked. The state machine
takes the name of the event, it prepends texton_to the name string and
then it looks up to event handler method.Example: If an event namedtoggleis created and sent to a state machine,
the target state machine will lookup for a method namedon_togglein the
current state instance.Since the event name directly impacts which state instance method will be called
the name of events must follow the Python identifier naming rules; please refer
tohttps://docs.python.org/3.3/reference/lexical_analysis.html#identifiersfor
more details.ok_event=fsm.Event('some_event_with_long_name')bad_event=fsm.Event('you cannot use spaces, @, $ and% here')TimersTimers are used to generate time events:After: Means an event will be generated after elapsed time.Every: Means an event will be generated every period of time.To generate the events useAfterandEveryobjects:@fsm.DeclareState(BlinkyFsm)classInitialization(fsm.State):defon_init(self):self.blinking=fsm.Every(1.0,'blink')returnStateOnThis line will generate an event namedblinkevery 1.0 seconds. To stop the
timer use:@fsm.DeclareState(BlinkyFsm)classStateOn(fsm.State):defon_entry(self):print('on')self.blinking.cancel()Second approach to cancel a running timer is by using eventtimerattribute.
When a timer generates an event it will automatically create event attribute
calledtimer. With this attribute you can also access the originating timer
through event. To stop the timer through an event see the example below:@fsm.DeclareState(BlinkyFsm)classStateOn(fsm.State):defon_blink(self,event):event.timer.cancel()# Stop the originating timerreturnStateOffStateA state is a description of the status of a system that is waiting to execute
a transition.State machineA finite-state machine (FSM) is a mathematical model of computation. It is an
abstract machine that can be in exactly one of a finite number of states at any
given time. The FSM can change from one state to another in response to some
external events; the change from one state to another is called a state
transition. An FSM is defined by a list of its states, its initial state, and
the conditions for each transition.State transitionSwitching from one state to another is called state transition. A transition is
a set of actions to be executed when a condition is fulfilled or when an event
is received.Transitions are started by returning target state class in an event handler.defon_some_event(self,event):do_some_stuff()returnSomeOtherState# Note: return a class object, not instance objectHierarchical Finite State Machines (HFSM)Please, refer to Wikipedia article for further explanation:https://en.wikipedia.org/wiki/UML_state_machine#Hierarchically_nested_statesSourceSource is available at github:https://github.com/nradulovic/pyedsOther linksThe following is a list of links to tools used by the project:Sphinx(used to build documentation):http://www.sphinx-doc.org/en/stable/setuptools(used for installing from source):https://setuptools.readthedocs.io/en/latest/