Package: pyrarchical
Pyrarchical State MachinePyrarchical state machine is a framework for a responsive heirarchical state-machine. An HSM built with this framework provides callbacks to execute functionality as the HSM responds to dispatched events. The PHSM loosely follows a hybrid dynamic system model allowing for a persistant analog state held by the HSM in addition to discrete state (which may be heirarchical). Your HSM is constructed through inheritance of the base functionality.The simplest two-state model with no heirarchy is shown here with more complete examples later on.fromsrc.pyrarchicalimporthsmclassTwoState(hsm.HSM):top=hsm.State("top")s1=hsm.State("s1")s2=hsm.State("s2")def__init__(self):self.x.append(0)self.top.configure(parent=None,init=self.s1,transitions=(hsm.Transition(event=Events.RESET,target=self.s1),))self.s1.configure(parent=self.top,transitions=(hsm.Transition(event=Events.FLIP,target=self.s2),))self.s2.configure(parent=self.top,transitions=(hsm.Transition(event=Events.FLOP,target=self.s1),))super().__init__(self.top)classEvents(hsm.Event):FLIP="flip"FLOP="flop"RESET="reset"if__name__=="__main__":importlogginglogging.basicConfig(level=logging.INFO)flip_flop=TwoState()flip_flop.dispatch(Events.FLIP)flip_flop.dispatch(Events.FLOP)flip_flop.dispatch(Events.FLIP)flip_flop.dispatch(Events.RESET)DescriptionThis module provides a framework for a responsive heirarchical state-machine.For hybrid dynamical systems, the following update equations are commonly used.x[k+1] = f_s(x[k], u(k]))y[k] = g_s(x[k], u(k]))where f and g are functions specific to each discrete state s, x is a
continuous state that exists in all discrete states, u is a set of inputs, and
y is the output of the system.In a Python HSM implementation, only the during state will really obey the
discrete-time step, k. However, we may need to update states during other HSM
actions.x = init_s(x, u)
x = entry_s(x, u)
x = during_s(x, u)
x = exit_s(x, u)
y = output_s(x, u)The continuous state, x, is a member variable of the HSM. The output function
is a static method. The input, continuous state, and output each need an
object defined to describe them. At the simplest, this will just be a list.Getting StartedThe only file needed to use the state-machine is hsm.py. There is currently no installation. Copy the file to your active project to use it.Prerequisiteshsm.py is only implemented in Python3 using standard Python libraries (enum and logging).Installinggit clone git@gitlab.com:roelle/pyrarchical-state-machine.git
cp pyrarchical-state-machine/hsm.py ../your-project/hsm.pyRunning the testsRunninghsm.pywill run an example. Continupous integration tests are run by executinghsm_test.pyin thepyrarchical-state-machinedirectory.Break down into end to end testsTo test, you need a heirarchincal state state-machine. The state machine shown in this figure is described in the following code. This particular implementation is based on an example offers a comprehensive set of tests of all types of transitions.fromsrc.pyrarchicalimporthsmfromhashlibimportmd5classS211(hsm.State):defdefault_exit_action(self,x,u):hsm._log.info(self._name+" : Exit")returnxclassEvents(hsm.Event):A="a"B="b"C="c"D="d"E="e"F="f"G="g"H="h"classExample(hsm.HSM):s0=hsm.State("s0")s1=hsm.State("s1")s11=hsm.State("s11")s2=hsm.State("s2")s21=hsm.State("s21")s211=S211("s211")def__init__(self):self.x.append(md5())self.s0.configure(parent=None,init=self.s1,transitions=(hsm.Transition(event=Events.E,target=self.s211),))self.s1.configure(parent=self.s0,init=self.s11,transitions=(hsm.Transition(event=Events.A,target=self.s1),hsm.Transition(event=Events.B,target=self.s11),hsm.Transition(event=Events.C,target=self.s2),hsm.Transition(event=Events.D,target=self.s0),hsm.Transition(event=Events.F,target=self.s211)))self.s11.configure(parent=self.s1,transitions=(hsm.Transition(event=Events.G,target=self.s211),))self.s2.configure(parent=self.s0,init=self.s21,transitions=(hsm.Transition(event=Events.C,target=self.s1),hsm.Transition(event=Events.F,target=self.s11)))self.s21.configure(parent=self.s2,init=self.s211,transitions=(hsm.Transition(event=Events.B,target=self.s211),hsm.Transition(event=Events.H,target=self.s21)))self.s211.configure(parent=self.s21,transitions=(hsm.Transition(event=Events.B,target=self.s21),hsm.Transition(event=Events.D,target=self.s21),hsm.Transition(event=Events.G,target=self.s0),hsm.Transition(guard=self.s211_s11_guard,target=self.s11)))self.s0.init_action=lambdax,u:self.update(self.x,b"s0_init")self.s0.entry_action=lambdax,u:self.update(self.x,b"s0_entry")self.s0.exit_action=lambdax,u:self.update(self.x,b"s0_exit")self.s0.during_action=lambdax,u:self.update(self.x,b"s0_during")self.s1.init_action=lambdax,u:self.update(self.x,b"s1_init")self.s1.entry_action=lambdax,u:self.update(self.x,b"s1_entry")self.s1.exit_action=lambdax,u:self.update(self.x,b"s1_exit")self.s1.during_action=lambdax,u:self.update(self.x,b"s1_during")self.s11.init_action=lambdax,u:self.update(self.x,b"s11_init")self.s11.entry_action=lambdax,u:self.update(self.x,b"s11_entry")self.s11.exit_action=lambdax,u:self.update(self.x,b"s11_exit")self.s11.during_action=lambdax,u:self.update(self.x,b"s11_during")self.s2.init_action=lambdax,u:self.update(self.x,b"s2_init")self.s2.entry_action=lambdax,u:self.update(self.x,b"s2_entry")self.s2.exit_action=lambdax,u:self.update(self.x,b"s2_exit")self.s2.during_action=lambdax,u:self.update(self.x,b"s2_during")self.s21.init_action=lambdax,u:self.update(self.x,b"s21_init")self.s21.entry_action=lambdax,u:self.update(self.x,b"s21_entry")self.s21.exit_action=lambdax,u:self.update(self.x,b"s21_exit")self.s21.during_action=lambdax,u:self.update(self.x,b"s21_during")self.s211.init_action=lambdax,u:self.update(self.x,b"s211_init")self.s211.entry_action=lambdax,u:self.update(self.x,b"s211_entry")self.s211.exit_action=lambdax,u:self.update(self.x,b"s211_exit")self.s211.during_action=lambdax,u:self.update(self.x,b"s211_during")super().__init__(self.s0)@staticmethoddefs211_s11_guard(x,u):try:returnu>10.0except:returnFalse@staticmethoddefupdate(x,u):x[0].update(u)returnxThis machine may be tested for any transitiontest_hsm=Example()test_hsm.x[0]=md5()# Reset the hsm continuous statetest_hsm.dispatch(Events.E)x=[md5()]x=Example.update(x,b"s11_exit")x=Example.update(x,b"s1_exit")x=Example.update(x,b"s2_entry")x=Example.update(x,b"s21_entry")x=Example.update(x,b"s211_entry")x[0].digest==test_hsm.x[0].digestVersioningWe useSemVerfor versioning. For the versions available, see thetags on this repository.AuthorsMatt Roelle-Initial work-roelleSee also the list ofcontributorswho participated in this project.LicenseThis project is licensed under the MIT License - see theLICENSE.mdfile for detailsAcknowledgmentsThis READMEtemplatewas copied fromPurpleBoothMany discussions about heirarchical-state machines informed this work.