Skip to content

Commit b35dd86

Browse files
authored
Introduced Post Conditions with State restored (#10)
Reworked Context and Expression Token
1 parent a8918db commit b35dd86

File tree

5 files changed

+274
-38
lines changed

5 files changed

+274
-38
lines changed

sample/sample5.xml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?xml version="1.0"?>
2+
<x:States xmlns:x="pystatemachine:sm">
3+
<State>
4+
<Name>Enter</Name>
5+
<Event>
6+
<Name>ToExit</Name>
7+
<ToState>Exit</ToState>
8+
<PreConditions>
9+
<Condition>
10+
<Expression>testBaseStateMachine.everFalse</Expression>
11+
<Result>False</Result>
12+
</Condition>
13+
<Condition>
14+
<Expression>testBaseStateMachine.test</Expression>
15+
<Result>2</Result>
16+
</Condition>
17+
</PreConditions>
18+
<PreActions>
19+
<Action>
20+
<Expression>testBaseStateMachine.testPrint</Expression>
21+
</Action>
22+
</PreActions>
23+
</Event>
24+
</State>
25+
<State>
26+
<Name>Exit</Name>
27+
<Event>
28+
<Name>ToNull</Name>
29+
<ToState>Null</ToState>
30+
<PreConditions>
31+
<Condition>
32+
<Expression>testBaseStateMachine.everTrue</Expression>
33+
<Result>True</Result>
34+
</Condition>
35+
</PreConditions>
36+
<PostActions>
37+
<Action>
38+
<Expression>testBaseStateMachine.testPrint</Expression>
39+
</Action>
40+
</PostActions>
41+
<PostActions>
42+
<Action>
43+
<Expression>testBaseStateMachine.setTestTo3</Expression>
44+
</Action>
45+
<Action>
46+
<Expression>testBaseStateMachine.printTest</Expression>
47+
</Action>
48+
</PostActions>
49+
<PostConditions>
50+
<Condition>
51+
<Expression>testBaseStateMachine.test</Expression>
52+
<Result>3</Result>
53+
</Condition>
54+
</PostConditions>
55+
</Event>
56+
</State>
57+
<State>
58+
<Name>Null</Name>
59+
<Event>
60+
<Name>ToNull</Name>
61+
<ToState>Null</ToState>
62+
<PostActions>
63+
<Action>
64+
<Expression>testBaseStateMachine.testPrint</Expression>
65+
</Action>
66+
</PostActions>
67+
</Event>
68+
</State>
69+
<Initial_State>Enter</Initial_State>
70+
</x:States>

sample/sample6.xml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0"?>
2+
<x:States xmlns:x="pystatemachine:sm">
3+
<State>
4+
<Name>Enter</Name>
5+
<Event>
6+
<Name>ToExit</Name>
7+
<ToState>Exit</ToState>
8+
<PreConditions>
9+
<Condition>
10+
<Expression>testBaseStateMachine.everFalse</Expression>
11+
<Result>False</Result>
12+
</Condition>
13+
<Condition>
14+
<Expression>testBaseStateMachine.test</Expression>
15+
<Result>2</Result>
16+
</Condition>
17+
</PreConditions>
18+
<PreActions>
19+
<Action>
20+
<Expression>testBaseStateMachine.testPrint</Expression>
21+
</Action>
22+
</PreActions>
23+
</Event>
24+
</State>
25+
<State>
26+
<Name>Exit</Name>
27+
<Event>
28+
<Name>ToNull</Name>
29+
<ToState>Null</ToState>
30+
<PreConditions>
31+
<Condition>
32+
<Expression>testBaseStateMachine.everTrue</Expression>
33+
<Result>True</Result>
34+
</Condition>
35+
</PreConditions>
36+
<PostActions>
37+
<Action>
38+
<Expression>testBaseStateMachine.testPrint</Expression>
39+
</Action>
40+
</PostActions>
41+
<PostActions>
42+
<Action>
43+
<Expression>testBaseStateMachine.printTest</Expression>
44+
</Action>
45+
</PostActions>
46+
<PostConditions>
47+
<Condition>
48+
<Expression>testBaseStateMachine.test</Expression>
49+
<Result>3</Result>
50+
</Condition>
51+
</PostConditions>
52+
</Event>
53+
</State>
54+
<State>
55+
<Name>Null</Name>
56+
<Event>
57+
<Name>ToNull</Name>
58+
<ToState>Null</ToState>
59+
<PostActions>
60+
<Action>
61+
<Expression>testBaseStateMachine.testPrint</Expression>
62+
</Action>
63+
</PostActions>
64+
</Event>
65+
</State>
66+
<Initial_State>Enter</Initial_State>
67+
</x:States>

src/ReadStateMachine.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,20 @@ def ReadStateMachineFile(xml_file : str):
6161
pre_conditions = Conditions(pre_condition_elements)
6262
elif event_child.tag == "PostConditions":
6363
# State->Event->PostConditions Element
64+
post_condition_elements = []
6465
for postconditions_child in event_child:
6566
#print(postconditions_child.text)
6667
# State->Event->PostConditions->Condition Element
67-
None
68+
if postconditions_child.tag == "Condition":
69+
expression = ""
70+
result = ""
71+
for condition_child in postconditions_child:
72+
if condition_child.tag == "Expression":
73+
expression = condition_child.text
74+
elif condition_child.tag == "Result":
75+
result = condition_child.text
76+
post_condition_elements.append(Condition(expression,result))
77+
post_conditions = Conditions(post_condition_elements)
6878
elif event_child.tag == "PreActions":
6979
# State->Event->PreActions Element
7080
pre_action_elements = []
@@ -91,7 +101,7 @@ def ReadStateMachineFile(xml_file : str):
91101
if action_child.tag == "Expression":
92102
expression = action_child.text
93103
post_action_elements.append(Action(expression))
94-
post_actions = Actions(pre_action_elements)
104+
post_actions = Actions(post_action_elements)
95105

96106
events[event_name] = Event(event_name,to_state,pre_conditions,post_conditions,pre_actions,post_actions)
97107
#print(event.to_string())

src/StateMachine.py

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,32 @@ def __init__(self, xml_file : str):
99
self.states = None
1010
self.current_state = ""
1111
self.context = {}
12+
self.saved_state = None
1213
logging.basicConfig(filename=xml_file.split(sep=".xml")[0] + '.log',format='%(asctime)s - %(levelname)s - %(message)s', level=logging.DEBUG)
1314

1415
def __CheckConditions(self,conditions):
1516
all_conditions_satisfied = True
1617
if(conditions != None):
1718
_conditions = conditions.conditions
1819
for condition in _conditions:
19-
if condition.expression in self.context:
20-
func = self.context[condition.expression]
21-
result = None
22-
if callable(func):
23-
result = func()
24-
else:
25-
result = func
26-
if str(result) != condition.result:
27-
all_conditions_satisfied = False
28-
break
20+
module,expression = self.__PrepareExpression(condition.expression)
21+
if module in self.context:
22+
mod = self.context[module]
23+
try:
24+
func = getattr(mod, expression)
25+
result = None
26+
if callable(func):
27+
result = func()
28+
else:
29+
result = func
30+
if str(result) != condition.result:
31+
all_conditions_satisfied = False
32+
break
33+
except:
34+
logging.error("Not Found Expression %s in Context", condition.expression)
35+
all_conditions_satisfied = False
2936
else:
30-
logging.error("No Found Condition Expression %s in Context", condition.expression)
37+
logging.error("Not Found Module %s in Context", module)
3138
all_conditions_satisfied = False
3239
else:
3340
logging.info("No Condition")
@@ -38,20 +45,36 @@ def __ExecActions(self,actions):
3845
if(actions != None):
3946
_actions = actions.actions
4047
for action in _actions:
41-
if action.expression in self.context:
42-
func = self.context[action.expression]
43-
if callable(func):
44-
#print("Call ",action.expression)
45-
func()
46-
else:
47-
func
48+
module,expression = self.__PrepareExpression(action.expression)
49+
if module in self.context:
50+
mod = self.context[module]
51+
try:
52+
func = getattr(mod, expression)
53+
if callable(func):
54+
func()
55+
else:
56+
func
57+
except:
58+
logging.error("Not Found Expression %s in Context", action.expression)
59+
all_action_executed = False;
4860
else:
49-
logging.error("No Found Action Expression %s in Context", action.expression)
61+
logging.error("Not Found Module %s in Context", module)
5062
all_action_executed = False;
5163
else:
5264
logging.info("No Action")
5365
return all_action_executed
5466

67+
def __SaveState(self):
68+
self.saved_state = [self.current_state, self.context]
69+
70+
def __RestoreState(self):
71+
self.current_state = self.saved_state[0]
72+
self.context = self.saved_state[1]
73+
74+
def __PrepareExpression(self,expression):
75+
module_expression=expression.rsplit('.',1)
76+
return module_expression[0],module_expression[1]
77+
5578
def get_current_state(self):
5679
return self.current_state
5780

@@ -63,35 +86,45 @@ def LoadStateMachine(self):
6386
self.states , self.current_state = ReadStateMachineFile(self.xml_file)
6487
logging.info('State Machine Loaded')
6588

66-
def addVariableToContext(self, module : str, variable : str):
67-
mod = __import__(module)
68-
func = getattr(mod, variable)
69-
self.context[module+"."+variable] = func
89+
def addModuleToContext(self, module : str):
90+
mod = __import__(module)
91+
self.context[module] = mod
7092

7193
def InjectEvent(self, event : str):
7294
my_state = self.states[self.current_state]
7395
possible_events = my_state.events
7496
if event in possible_events:
7597
handled_event = possible_events[event]
98+
## Save Old State
99+
self.__SaveState()
76100
## Preconditions
77101
all_pre_conditions_satisfied = self.__CheckConditions(handled_event.pre_conditions)
78102
if(all_pre_conditions_satisfied):
103+
logging.debug("PreConditions satisfied")
79104
## Preactions
80105
all_pre_actions_executed = self.__ExecActions(handled_event.pre_actions)
81106
if(all_pre_actions_executed):
82-
## Save Old State
107+
logging.debug("PreActions Executed")
83108
## Transition
84109
logging.debug("Transition %s ------> %s", self.current_state, handled_event.to_state)
85110
self.current_state = handled_event.to_state
86111
## Postactions
87112
all_post_actions_executed = self.__ExecActions(handled_event.post_actions)
88113
if(all_post_actions_executed):
89-
None
114+
logging.debug("PostActions Executed")
90115
## Postconditions
116+
all_post_conditions_satisfied = self.__CheckConditions(handled_event.post_conditions)
117+
if(all_post_conditions_satisfied):
118+
logging.debug("PostConditions Satisfied")
119+
else:
120+
logging.error("Not all PostConditions satisfied, restore saved state")
121+
self.__RestoreState()
91122
else:
92-
logging.error("Not all PostActions Executed")
123+
logging.error("Not all PostActions Executed, restore saved state")
124+
self.__RestoreState()
93125
else:
94-
logging.error("Not all PretActions Executed")
126+
logging.error("Not all PretActions Executed, restore saved state")
127+
self.__RestoreState()
95128
else:
96129
logging.error("Not all PreConditions satisfied")
97130
else:

0 commit comments

Comments
 (0)