Source code for examples.TestHFSM.symplehfsm_demo
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
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 SympleDictHFSM
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 = SympleDictHFSM(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 = MyStateMachine.sm_structure.states
[docs] def tearDown(self):
self.state_machine.exit()
[docs] def test_initial_transition_state(self):
# init: s0 -> s11: s0.entry, s1.entry, s11.entry
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()