Source code for examples.TestHFSM.symplehfsm_demo

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""

This is a full blown example. It uses all features of the symplehfsm framework (entry-, exit- and 
transition-actions, guards). There are also tests for each transition. This example shows the usage
of different actions implementations applied to the same state machine (one for testing, one for 
the interactive demo).


It's all about:
::

    making a statemachine testable based on: http://accu.org/index.php/journals/1548


    ------------------------------------------------------------------------------

    Statechart used to test the SympleHFSM
    based on 
    [Samek] Miro Samek, Practical Statecharts in C/C++, CMP Books 2002. 
    There's a companion website with additional information: http://www.quantum-leaps.com
    taken from: http://accu.org/index.php/journals/252

    see also: http://en.wikipedia.org/wiki/UML_state_machine#Local_versus_external_transitions
    making a statemachine testable based on: http://accu.org/index.php/journals/1548

              +-------------------------------------------------------------------------------------+
    *--init-->|                                         s0                                          |
              +-------------------------------------------------------------------------------------+
              | entry/                                                                              |
              | exit/                                                                               |
              |       +-------------------------+    +------------------------------------------+   |
              |   *-->|        s1               |    |               s2                         |   |
              |       +-------------------------+    +------------------------------------------+   |
              |       | entry/                  |-c->| entry/              +-----------------+  |   |
              |<--d---| exit/                   |    | exit/               | h[!foo]/ foo=1; |  |   |
              |       |     +---------------+   |<-c-|      +----------------------------+   |  |   |
              |       | *-->|      s11      |   |    |  *-->|             s21            |<--+  |   |
              |       |     +---------------+   |    |      +----------------------------+      |   |
              |    +--|     | entry/        |   |    |      | entry/                     |      |   |
              |   a|  |     | exit/         |<---f---|      | exit/                      |      |   |
              |    +->|     | h[foo]/ foo=0;|   |    |      |       +--------------+     |      |   |
              |       |     |               |   |    |      |   *-->|     s211     |     |      |   |
              |       |--b->|               |   |    |      |       +--------------+     |      |   |
              |       |     |               |   |--------f--------->| entry/       |     |      |   |
              |       |     |               |   |    |      |       | exit/        |--------g------>|
              |       |     |               |----------g----------->|              |     |      |   |
              |       |     |               |   |    |      |--b--->|              |<-------e-------|
              |       |     |               |   |    |      |<---d--|              |     |      |   |
              |       |     |               |   |    |      |       +--------------+     |      |   |
              |       |     +---------------+   |    |      +----------------------------+      |   |--exit-->O
              |       |                         |    |                                          |   |
              |       +-------------------------+    +------------------------------------------+   |
              |                                                                                     |
              +-------------------------------------------------------------------------------------+


      As far as I understand it, current_state always points to either s11 or s211 (one of the leaf states).
      Also for the transitions triggered by an event I assume it works as follows:

      event| from -> to | transition actions
       init:  s0 ->  s11:   s0.entry, s1.entry, s11.entry
       exit:s211 ->   s0: s211.exit, s21.exit, s2.exit, s0.exit
             s11 ->   s0:  s11.exit, s1.exit, s0.exit
          a:  s1 ->   s1:  s11.exit,  s1.exit,  s1.entry, s11.entry
          b:  s1 ->  s11:  s11.exit, s11.entry
             s21 -> s211: s211.exit, s211.entry
          c:  s1 ->   s2:  s11.exit,  s1.exit,  s2.entry, s21.entry, s211.entry
              s2 ->   s1: s211.exit, s21.exit,  s2.exit, s1.entry, s11.entry
          d:  s1 ->   s0:  s11.exit,  s1.exit,  s1.entry, s11.entry
            s211 ->  s21: s211.exit, s211.entry
          e:  s0 -> s211:  s11.exit,  s1.exit, s2.entry, s21.entry, s211.entry
                          s211.exit, s21.exit,  s2.exit, s2.entry, s21.entry, s211.entry
          f:  s2 ->  s11: s211.exit, s21.exit,  s2.exit, s1.entry, s11.entry
              s1 -> s211:  s11.exit,  s1.exit,  s2.entry, s21.entry, s211.entry
          g: s11 -> s211:  s11.exit,  s1.exit,  s2.entry, s21.entry, s211.entry
            s211 ->   s0: s211.exit, s21.exit,  s2.exit,  s1.entry, s11.entry
          h: s11 foo==True: actions.unset_foo
             s11 foo==False: do nothing
             s21 foo==False: s211.exit, s21.exit, actions.set_foo, s21.entry, s211.entry
             s21 foo==True: do nothing
          
      Actions:
          set_foo() => foo = 1
          unset_foo() => foo = 0

    The action specifications shown are:

       *

         The transition from s21 to itself (a self-transition). This is an example of a transition 
         that has a guard (in brackets []) and an associated action (after the slash /). The guard 
         is a condition that must evaluate to true to enable the transition. If it evaluates to false, 
         the transition is not taken and none of the actions are executed. A self-transition exits and 
         reenters the state, hence the associated exit and entry actions are executed.
       *

         The internal transition inside s11 is not drawn with an arrow. It merely specifies an action 
         that is to be taken when a certain event occurs, but no transition to another state occurs, 
         and no exit or entry actions are performed. In our case the internal transition has a guard, 
         so the associated action (foo = 0) is only executed when the h key is pressed while foo 
         evaluates to true.


..todo: loop action back to event -> queue?

"""

__version__ = "1.0.3.0"
__author__ = "dr0iddr0id {at} gmail [dot] com (C) 2012"

import operator

import symplehfsm
from symplehfsm import SympleHFSM
from symplehfsm import Structure
from symplehfsm import BaseHFSMTests


# ------------------------------------------------------------------------------

[docs]class Actions(object): """ The Actions the statemachine can execute. """
[docs] def set_foo(self): raise NotImplementedException()
[docs] def unset_foo(self): raise NotImplementedException()
[docs] def check_foo(self): raise NotImplementedError("check_foo needs to be overridden to return bool")
[docs] def check_foo_inverted(self): raise NotImplementedError("check_foo_inverted needs to be overridden to return !check_foo") # following are just to prove it works correctly
[docs] def enter_s0(self): raise NotImplementedException()
[docs] def exit_s0(self): raise NotImplementedException()
[docs] def enter_s1(self): raise NotImplementedException()
[docs] def exit_s1(self): raise NotImplementedException()
[docs] def enter_s11(self): raise NotImplementedException()
[docs] def exit_s11(self): raise NotImplementedException()
[docs] def enter_s2(self): raise NotImplementedException()
[docs] def exit_s2(self): raise NotImplementedException()
[docs] def enter_s21(self): raise NotImplementedException()
[docs] def exit_s21(self): raise NotImplementedException()
[docs] def enter_s211(self): raise NotImplementedException()
[docs] def exit_s211(self): raise NotImplementedException()
[docs] def trans_s1_to_s1_a(self): raise NotImplementedException()
[docs] def trans_s1_to_s11_b(self): raise NotImplementedException()
[docs] def trans_s21_to_s211_b(self): raise NotImplementedException()
[docs] def trans_s1_to_s2_c(self): raise NotImplementedException()
[docs] def trans_s2_to_s1_c(self): raise NotImplementedException()
[docs] def trans_s1_to_s0_d(self): raise NotImplementedException()
[docs] def trans_s211_to_s21_d(self): raise NotImplementedException()
[docs] def trans_s0_to_s211_e(self): raise NotImplementedException()
[docs] def trans_s1_to_s211_f(self): raise NotImplementedException()
[docs] def trans_s2_to_s11_f(self): raise NotImplementedException()
[docs] def trans_s11_to_s211_g(self): raise NotImplementedException()
[docs] def trans_s211_to_s0_g(self): raise NotImplementedException()
[docs] def trans_s11_to_s11_h(self): raise NotImplementedException()
[docs] def trans_s21_to_s21_h(self): raise NotImplementedException() # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[docs]class EventEnum(object): """ Event identifiers of the statemachine (the events it can handle). """ # just make sure that the values are unique! a = 0 b = 1 c = 2 d = 3 e = 4 f = 5 g = 6 h = 7 # make it read only
EventEnum.__setattr__ = None # ------------------------------------------------------------------------------ # REQUIREMENTS: # - events interface # - actions interface (usage using different action interface implementations for test purposes) # - one state declaration/instanciation for each sm-type (sharing states as static datastructure since state itself are stateless) # - hierarchical states # - automatic calling entry/exits in a transition # - testable # PROS: # - simpler to setup # - less error prone # - less line of code to write # CONS: # - identifiers are strings (not necesairly), maybe 'enums' # - events are identifiers ('enums') # - typesafety from operator import methodcaller
[docs]class MyStateMachine(object): sm_structure = Structure() # state, parent, initial, entry, exit sm_structure.add_state("s0", None, False, methodcaller(Actions.enter_s0.__name__), methodcaller(Actions.exit_s0.__name__)) sm_structure.add_state( "s1", "s0", True, methodcaller("enter_s1"), methodcaller("exit_s1")) sm_structure.add_state( "s11", "s1", True, methodcaller("enter_s11"), methodcaller("exit_s11")) sm_structure.add_state( "s2", "s0", False, methodcaller("enter_s2"), methodcaller("exit_s2")) sm_structure.add_state( "s21", "s2", True, methodcaller("enter_s21"), methodcaller("exit_s21")) sm_structure.add_state( "s211", "s21", True, methodcaller("enter_s211"), methodcaller("exit_s211")) # handling state, event, next state, action, guard sm_structure.add_trans("s0", "e", "s211", methodcaller(Actions.trans_s0_to_s211_e.__name__), None) sm_structure.add_trans( "s1", "a", "s1", methodcaller("trans_s1_to_s1_a"), None) sm_structure.add_trans( "s1", "b", "s11", methodcaller("trans_s1_to_s11_b"), None) sm_structure.add_trans( "s1", "c", "s2", methodcaller("trans_s1_to_s2_c"), None) sm_structure.add_trans( "s1", "d", "s0", methodcaller("trans_s1_to_s0_d"), None) sm_structure.add_trans( "s1", "f", "s211", methodcaller("trans_s1_to_s211_f"), None) sm_structure.add_trans( "s11", "g", "s211", methodcaller("trans_s11_to_s211_g"), None) sm_structure.add_trans( "s11", "h", None, methodcaller("trans_s11_to_s11_h"), methodcaller("check_foo")) sm_structure.add_trans( "s2", "c", "s1", methodcaller("trans_s2_to_s1_c"), None) sm_structure.add_trans( "s2", "f", "s11", methodcaller("trans_s2_to_s11_f"), None) sm_structure.add_trans( "s21", "b", "s211", methodcaller("trans_s21_to_s211_b"), None) sm_structure.add_trans( "s21", "h", "s21", methodcaller("trans_s21_to_s21_h"), methodcaller("check_foo_inverted"), "s211-h") sm_structure.add_trans( "s211", "d", "s21", methodcaller("trans_s211_to_s21_d"), None) sm_structure.add_trans( "s211", "g", "s0", methodcaller("trans_s211_to_s0_g"), None) def __init__(self, actions): self.sm = SympleHFSM(self.sm_structure, actions)
[docs] def init(self): self.sm.init()
[docs] def exit(self): self.sm.exit()
[docs] def set_state(self, new_state): self.sm.set_state(new_state)
def _get_current_state(self): return self.sm.current_state current_state = property(_get_current_state)
[docs] def a(self): self.sm.handle_event("a")
[docs] def b(self): self.sm.handle_event("b")
[docs] def c(self): self.sm.handle_event("c")
[docs] def d(self): self.sm.handle_event("d")
[docs] def e(self): self.sm.handle_event("e")
[docs] def f(self): self.sm.handle_event("f")
[docs] def g(self): self.sm.handle_event("g")
[docs] def h(self): self.sm.handle_event("h") # ------------------------------------------------------------------------------
[docs]class SympleHFSMTests(BaseHFSMTests): """ Testcases for MyStateMachine using the BaseHFSMTests as base. """ # -- inner classes ---#
[docs] class TActions(Actions): """Test Actions for testing, captures all actions for comparison"""
[docs] class AEnum(object): """Define an 'enum' to have comparable values for each action""" # make sure each variable has a unique value! SETFOO = "SETFOO" UNSETFOO = "UNSETFOO" CHECKFOO = "CHECKFOO" ENTERS0 = "ENTERS0" EXITS0 = "EXITS0" ENTERS1 = "ENTERS1" EXITS1 = "EXITS1" ENTERS11 = "ENTERS11" EXITS11 = "EXITS11" ENTERS2 = "ENTERS2" EXITS2 = "EXITS2" ENTERS21 = "ENTERS21" EXITS21 = "EXITS21" ENTERS211 = "ENTERS211" EXITS211 = "EXITS211" TRANS_S1_TO_S1_A = "TRANS_S1_TO_S1_A" TRANS_S1_TO_S11_B = "TRANS_S1_TO_S11_B" TRANS_S21_TO_S211_B = "TRANS_S21_TO_S211_B" TRANS_S1_TO_S2_C = "TRANS_S1_TO_S2_C" TRANS_S2_TO_S1_C = "TRANS_S2_TO_S1_C" TRANS_S1_TO_S0_D = "TRANS_S1_TO_S0_D" TRANS_S211_TO_S21_D = "TRANS_S211_TO_S21_D" TRANS_S0_TO_S211_E = "TRANS_S0_TO_S211_E" TRANS_S1_TO_S211_F = "TRANS_S1_TO_S211_F" TRANS_S2_TO_S11_F = "TRANS_S2_TO_S11_F" TRANS_S11_TO_S211_G = "TRANS_S11_TO_S211_G" TRANS_S211_TO_S0_G = "TRANS_S211_TO_S0_G" TRANS_S11_TO_S11_H = "TRANS_S11_TO_S11_H" TRANS_S21_TO_S21_H = "TRANS_S21_TO_S21_H" # make it read only
AEnum.__setattr__ = None def __init__(self): self.captured_actions = [] self.foo = False # actions for guarded event/transition
[docs] def set_foo(self): self.captured_actions.append(self.AEnum.SETFOO)
[docs] def unset_foo(self): self.captured_actions.append(self.AEnum.UNSETFOO)
[docs] def check_foo(self): self.captured_actions.append(self.AEnum.CHECKFOO) return self.foo
[docs] def check_foo_inverted(self): return not self.check_foo() # following are just to prove it works correctly
[docs] def enter_s0(self): self.captured_actions.append(self.AEnum.ENTERS0)
[docs] def exit_s0(self): self.captured_actions.append(self.AEnum.EXITS0)
[docs] def enter_s1(self): self.captured_actions.append(self.AEnum.ENTERS1)
[docs] def exit_s1(self): self.captured_actions.append(self.AEnum.EXITS1)
[docs] def enter_s11(self): self.captured_actions.append(self.AEnum.ENTERS11)
[docs] def exit_s11(self): self.captured_actions.append(self.AEnum.EXITS11)
[docs] def enter_s2(self): self.captured_actions.append(self.AEnum.ENTERS2)
[docs] def exit_s2(self): self.captured_actions.append(self.AEnum.EXITS2)
[docs] def enter_s21(self): self.captured_actions.append(self.AEnum.ENTERS21)
[docs] def exit_s21(self): self.captured_actions.append(self.AEnum.EXITS21)
[docs] def enter_s211(self): self.captured_actions.append(self.AEnum.ENTERS211)
[docs] def exit_s211(self): self.captured_actions.append(self.AEnum.EXITS211)
[docs] def separator(self): self.captured_actions.append(self.AEnum._I) # transition acctions
[docs] def trans_s1_to_s1_a(self): self.captured_actions.append(self.AEnum.TRANS_S1_TO_S1_A)
[docs] def trans_s1_to_s11_b(self): self.captured_actions.append(self.AEnum.TRANS_S1_TO_S11_B)
[docs] def trans_s21_to_s211_b(self): self.captured_actions.append(self.AEnum.TRANS_S21_TO_S211_B)
[docs] def trans_s1_to_s2_c(self): self.captured_actions.append(self.AEnum.TRANS_S1_TO_S2_C)
[docs] def trans_s2_to_s1_c(self): self.captured_actions.append(self.AEnum.TRANS_S2_TO_S1_C)
[docs] def trans_s1_to_s0_d(self): self.captured_actions.append(self.AEnum.TRANS_S1_TO_S0_D)
[docs] def trans_s211_to_s21_d(self): self.captured_actions.append(self.AEnum.TRANS_S211_TO_S21_D)
[docs] def trans_s0_to_s211_e(self): self.captured_actions.append(self.AEnum.TRANS_S0_TO_S211_E)
[docs] def trans_s1_to_s211_f(self): self.captured_actions.append(self.AEnum.TRANS_S1_TO_S211_F)
[docs] def trans_s2_to_s11_f(self): self.captured_actions.append(self.AEnum.TRANS_S2_TO_S11_F)
[docs] def trans_s11_to_s211_g(self): self.captured_actions.append(self.AEnum.TRANS_S11_TO_S211_G)
[docs] def trans_s211_to_s0_g(self): self.captured_actions.append(self.AEnum.TRANS_S211_TO_S0_G)
[docs] def trans_s11_to_s11_h(self): self.captured_actions.append(self.AEnum.TRANS_S11_TO_S11_H) self.unset_foo()
[docs] def trans_s21_to_s21_h(self): self.captured_actions.append(self.AEnum.TRANS_S21_TO_S21_H) self.set_foo() # -- transition tests ---#
[docs] def setUp(self): self.actions = self.TActions() # self.state_machine = MyStateMachine(self.actions) self.state_machine = MyStateMachine(self.actions) print('======') self.state_machine.init() self.states = dict(zip(MyStateMachine.sm_structure.states.keys(), MyStateMachine.sm_structure.states.keys()))
[docs] def tearDown(self): self.state_machine.exit()
[docs] def test_initial_transition_state(self): # init: s0 -> s11: s0.entry, s1.entry, s11.entry self.state_machine.exit() # make sure the state machine is uninitialized v = self.TestVector("init", self.states["s0"], self.state_machine.init, self.states["s11"], [self.TActions.AEnum.ENTERS0, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_exit_transition_from_s211(self): # exit:s211 -> s0: s211.exit, s21.exit, s2.exit, s0.exit v = self.TestVector("exit from s211", self.states["s211"], self.state_machine.exit, None, \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.EXITS0]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_exit_transition_from_s11(self): # s11 -> s0: s11.exit, s1.exit, s0.exit v = self.TestVector("exit from s11", self.states["s11"], self.state_machine.exit, None, \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.EXITS0]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s1_to_s1_event_a(self): # a: s1 -> s1: s11.exit, s1.exit, s1.entry, s11.entry v = self.TestVector("s1 to s1 event a", self.states["s11"], self.state_machine.a, self.states["s11"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S1_A ,self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s1_to_s11_event_b(self): # b: s1 -> s11: s11.exit, s1.exit, s1.entry, s11.entry v = self.TestVector("s1 to s11 event b", self.states["s11"], self.state_machine.b, self.states["s11"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.TRANS_S1_TO_S11_B , self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s21_to_s211_event_b(self): # s21 -> s211: s211.exit, s21.exit, s21.entry, s211.entry v = self.TestVector("s21 to s211 event b", self.states["s211"], self.state_machine.b, self.states["s211"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.TRANS_S21_TO_S211_B, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s1_to_s2_event_c(self): # c: s1 -> s2: s11.exit, s1.exit, s2.entry, s21.entry, s211.entry v = self.TestVector("s1 to s2 event c", self.states["s11"], self.state_machine.c, self.states["s211"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S2_C, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s2_to_s1_event_c(self): # s2 -> s1: s211.exit, s21.exit, s2.exit, s1.entry, s11.entry v = self.TestVector("s2 to s1 event c", self.states["s211"], self.state_machine.c, self.states["s11"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.TRANS_S2_TO_S1_C, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s1_to_s0_event_d(self): # d: s1 -> s0: s11.exit, s1.exit, s1.entry, s11.entry v = self.TestVector("s1 to s0 event d", self.states["s11"], self.state_machine.d, self.states["s11"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S0_D, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s211_to_s21_event_d(self): # s211 -> s21: s211.exit, s21.exit, s21.entry, s211.entry v = self.TestVector("s211 to s21 event d", self.states["s211"], self.state_machine.d, self.states["s211"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.TRANS_S211_TO_S21_D, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s0_to_s211_event_e_case_s11(self): # e: s0 -> s211: s11.exit, s1.exit, s2.entry, s21.entry, s211.entry v = self.TestVector("s0 to s211 event e case s11", self.states["s11"], self.state_machine.e, self.states["s211"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S0_TO_S211_E, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s0_t0_s211_event_e_case_s211(self): # s211.exit, s21.exit, s2.exit, s2.entry, s21.entry, s211.entry v = self.TestVector("s0 to s211 event e case s211", self.states["s211"], self.state_machine.e, self.states["s211"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.TRANS_S0_TO_S211_E, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s2_to_s11_event_f(self): # f: s2 -> s11: s211.exit, s21.exit, s2.exit, s1.entry, s11.entry v = self.TestVector("s2 to s11 event f", self.states["s211"], self.state_machine.f, self.states["s11"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.TRANS_S2_TO_S11_F, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s1_to_s211_event_f(self): # s1 -> s211: s11.exit, s1.exit, s2.entry, s21.entry, s211.entry v = self.TestVector("s1 to s211 event f", self.states["s11"], self.state_machine.f, self.states["s211"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S211_F, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s11_to_s211_event_g(self): # g: s11 -> s211: s11.exit, s1.exit, s2.entry, s21.entry, s211.entry v = self.TestVector("s11 to s211 event g", self.states["s11"], self.state_machine.g, self.states["s211"], \ [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S11_TO_S211_G, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s211_to_s0_event_g(self): # s211 -> s0: s211.exit, s21.exit, s2.exit, s1.entry, s11.entry v = self.TestVector("s211 to s0 event g", self.states["s211"], self.state_machine.g, self.states["s11"], \ [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.TRANS_S211_TO_S0_G, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s11_to_s11_event_h_guard_true(self): # h: s11 foo==True: actions.check_foo, actions.unset_foo self.actions.foo = True v = self.TestVector("test_s11_to_s11_event_h_guard_true", self.states["s11"], self.state_machine.h, self.states["s11"], \ [self.TActions.AEnum.CHECKFOO, self.TActions.AEnum.TRANS_S11_TO_S11_H, self.TActions.AEnum.UNSETFOO]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s11_to_s11_event_h_guard_false(self): # h: s11 foo==False: actions.check_foo self.actions.unset_foo = False v = self.TestVector("test_s11_to_s11_event_h_guard_false", self.states["s11"], self.state_machine.h, self.states["s11"], \ [self.TActions.AEnum.CHECKFOO]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s211_to_s211_event_h_guard_false(self): # s21 foo==False: s211.exit, s21.exit, actions.set_foo, s21.entry, s211.entry self.actions.foo = False v = self.TestVector("test_s211_to_s211_event_h_guard_false", self.states["s211"], self.state_machine.h, self.states["s211"], \ [self.TActions.AEnum.CHECKFOO, self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.TRANS_S21_TO_S21_H, self.TActions.AEnum.SETFOO, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s211_to_s211_event_h_guard_true(self): # s21 foo==False: s211.exit, s21.exit, actions.set_foo, s21.entry, s211.entry self.actions.foo = True v = self.TestVector("test_s211_to_s211_event_h_guard_true", self.states["s211"], self.state_machine.h, self.states["s211"], \ [self.TActions.AEnum.CHECKFOO]) self.prove_one_transition(self.state_machine, self.actions, v)
[docs] def test_s211_to_s211_event_a_no_guard(self): # s21 foo==False: s211.exit, s21.exit, actions.set_foo, s21.entry, s211.entry v = self.TestVector("test_s211_to_s211_event_a_no_guard", self.states["s211"], self.state_machine.a, self.states["s211"], \ []) self.prove_one_transition(self.state_machine, self.actions, v) # def test_sequence_AAA(self): # vecs = [ # # self.TestVector("s1 to s211 event f", self.states["s11"], self.state_machine.f, self.states["s211"], \ # # [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S211_F, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]), # # self.TestVector("s2 to s1 event c", self.states["s211"], self.state_machine.c, self.states["s11"], \ # # [self.TActions.AEnum.EXITS211, self.TActions.AEnum.EXITS21, self.TActions.AEnum.EXITS2, self.TActions.AEnum.TRANS_S2_TO_S1_C, self.TActions.AEnum.ENTERS1, self.TActions.AEnum.ENTERS11]), # self.TestVector("test_s11_to_s11_event_h_guard_false", self.states["s11"], self.state_machine.h, self.states["s11"], \ # [self.TActions.AEnum.CHECKFOO]), # self.TestVector("s1 to s2 event c", self.states["s11"], self.state_machine.c, self.states["s211"], \ # [self.TActions.AEnum.EXITS11, self.TActions.AEnum.EXITS1, self.TActions.AEnum.TRANS_S1_TO_S2_C, self.TActions.AEnum.ENTERS2, self.TActions.AEnum.ENTERS21, self.TActions.AEnum.ENTERS211]), # ] # self.prove_transition_sequence(self.state_machine, self.actions, vecs) # ------------------------------------------------------------------------------
[docs]class SympleHFSMTestsOptimized(SympleHFSMTests):
[docs] def setUp(self): MyStateMachine.sm_structure.do_optimize() SympleHFSMTests.setUp(self) # ------------------------------------------------------------------------------ # interactive demo of the same state machine, but using different actions implementation to print # out the actions
[docs]def demo(): """ The demo. This is the main method that runs the interactive demo. """ import sys class PrintActions(Actions): """ The Actions printing what they do. """ def __init__(self): self._foo = False def set_foo(self): print("\tfoo set") self._foo = True def unset_foo(self): print("\tfoo unset") self._foo = False def check_foo(self): print("\tchecking foo") return self._foo def check_foo_inverted(self): print("\tchecking !foo") return not self._foo # following are just to prove it works correctly def enter_s0(self): print("\tentering S0") def exit_s0(self): print("\texiting S0") def enter_s1(self): print("\tentering S1") def exit_s1(self): print("\texiting S1") def enter_s11(self): print("\tentering S11") def exit_s11(self): print("\texiting S11") def enter_s2(self): print("\tentering S2") def exit_s2(self): print("\texiting S2") def enter_s21(self): print("\tentering S21") def exit_s21(self): print("\texiting S21") def enter_s211(self): print("\tentering S211") def exit_s211(self): print("\texiting S211") # transition acctions def trans_s1_to_s1_a(self): print("\tTRANS_S1_TO_S1_A") def trans_s1_to_s11_b(self): print("\tTRANS_S1_TO_S11_B") def trans_s21_to_s211_b(self): print("\tTRANS_S21_TO_S211_B") def trans_s1_to_s2_c(self): print("\tTRANS_S1_TO_S2_C") def trans_s2_to_s1_c(self): print("\tTRANS_S2_TO_S1_C") def trans_s1_to_s0_d(self): print("\tTRANS_S1_TO_S0_D") def trans_s211_to_s21_d(self): print("\tTRANS_S211_TO_S21_D") def trans_s0_to_s211_e(self): print("\tTRANS_S0_TO_S211_E") def trans_s1_to_s211_f(self): print("\tTRANS_S1_TO_S211_F") def trans_s2_to_s11_f(self): print("\tTRANS_S2_TO_S11_F") def trans_s11_to_s211_g(self): print("\tTRANS_S11_TO_S211_G") def trans_s211_to_s0_g(self): print("\tTRANS_S211_TO_S0_G") def trans_s11_to_s11_h(self): print("\tTRANS_S11_TO_S11_H") self.unset_foo() def trans_s21_to_s21_h(self): print("\tTRANS_S21_TO_S21_H") self.set_foo() def print_help(): print("") print("usage:") print(" 'quit' : exits the demo") print(" 'help' : prints this help") print(" 'init' : init event to init the state machine") print(" 'exit' : exit event to exit the state machine, caution, no state set afterwards!") print(" 'print' : print the state chart") print(" events : 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'") print("") print("") if __debug__: import os print("use 'python -O "+os.path.split(__file__)[1]+"' to run the demo!") return print_help() actions = PrintActions() sm = MyStateMachine(actions) # setup event handlers event_handlers = {'a': sm.a, 'b':sm.b, 'c':sm.c, 'd':sm.d, 'e':sm.e, 'f':sm.f, 'g':sm.g, 'h':sm.h, 'exit':sm.exit, 'init':sm.init} running = True while running: prompt = str(sm.current_state.name if sm.current_state else "None") + " << " # python 3.x compatibility if sys.version_info < (3, ): evt = raw_input(prompt) else: evt = input(prompt) try: if evt == 'quit': running = False elif evt == 'help': print_help() elif evt == 'print': print_chart() else: if evt in list(event_handlers.keys()): # handle event in the statemachine event_handlers[evt]() else: print("not supported event: ", evt) except Exception as e: print("Error:" + str(e))
if __name__ == '__main__': demo()