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))
[docs]def print_chart():
print(" +--------------------------------------------------------------------------+")
print(" *-init->| s0 |")
print(" +--------------------------------------------------------------------------+")
print(" | entry/ |")
print(" | exit/ |")
print(" | +-------------------------+ +---------------------------------+ |")
print(" | *-->| s1 | | s2 | |")
print(" | +-------------------------+ +---------------------------------+ |")
print(" | | entry/ |-c->| entry/ +-----------------+ | |")
print(" |<-d--| exit/ | | exit/ | h[!foo]/ foo=1; | | |")
print(" | | +---------------+ |<-c-| +--------------------+ | | |")
print(" | | *-->| s11 | | | *-->| s21 |<-+ | |")
print(" | | +---------------+ | | +--------------------+ | |")
print(" | +--| | entry/ | | | | entry/ | | |")
print(" | a| | | exit/ |<---f---| | exit/ | | |")
print(" | +->| | h[foo]/ foo=0;| | | | +---------+ | | |")
print(" | | | | | | | *-->| s211 | | | |")
print(" | |--b->| | | | | +---------+ | | |")
print(" | | | | |--------f--------->| entry/ | | | |")
print(" | | | | | | | | exit/ |-----g----->|")
print(" | | | |----------g----------->| | | | |")
print(" | | | | | | |--b--->| |<----e------|")
print(" | | | | | | |<---d--| | | | |")
print(" | | | | | | | +---------+ | | |")
print(" | | +---------------+ | | +--------------------+ | |-exit->O")
print(" | | | | | |")
print(" | +-------------------------+ +---------------------------------+ |")
print(" | |")
print(" +--------------------------------------------------------------------------+")
if __name__ == '__main__':
demo()