-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathFSM.py
103 lines (86 loc) · 3.7 KB
/
FSM.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
Interface to an FSM module (graph) used by ProductModelProgram
"""
from model import Model
class FSM(Model):
def __init__(self, module, exclude, include):
Model.__init__(self, module, exclude, include)
def post_init(self):
"""
Now that all modules have been imported and executed their __init__
do a postprocessing pass
to process metadata that might be affected by configuration modules
"""
# Do all of this work here rather than in __init__
# so it can include the effects of any pymodel config modules
# Make copies of collections that may be altered later
# self.actions is not used in this module outside this __init__
# BUT it is used in several places in Model and ProductModelProgram
# EnabledTransitions below works directly on self.module.graph,
# not self.actions
if not hasattr(self.module, 'actions'):
self.actions = list(self.actions_in_graph()) # default, make copy
else:
self.actions = list(self.module.actions) # copy
Model.post_init(self) # uses self.actions
# Construct self.graph like self.module.graph
# BUT also accounts for include, exclude via self.actions
self.graph = [ (current, (action,args,result), next)
for (current, (action,args,result), next) in
self.module.graph if action in self.actions ]
# prepare for first run
self.current = self.module.initial # raise exception if module is not FSM
def actions_in_graph(self):
return tuple(set([ action for (current, (action,args,result), next) in
self.module.graph])) # not self.graph, here ONLY
def make_properties(self, state):
return { 'accepting': state in self.module.accepting, 'statefilter': True,
'stateinvariant': True }
def Properties(self):
return self.make_properties(self.current)
def Reset(self): # needed by stepper
self.current = self.module.initial
def CleanupGraph(self, cleanup=False):
"""
if cleanup, return the graph with just the cleanup transitions
"""
if cleanup:
graph = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if action in self.module.cleanup]
else:
graph = self.graph
return graph
def ActionEnabled(self, a, args):
"""
action a with args is enabled in the current state
"""
# no cleanup check here
# any args matches empty arguments in FSM
return any([(a == action and (not arguments or args == arguments))
for (current,(action, arguments, result),next)
in self.graph
if current == self.current ])
def EnabledTransitions(self, cleanup=False):
"""
Return list tuples for all enabled actions:
(action, args, next state, properties)
"""
graph = self.CleanupGraph(cleanup)
return [(action, args, result, next, self.make_properties(next))
for (current,(action,args,result),next) in graph
if current == self.current ]
def DoAction(self, a, arguments):
ts = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if current == self.current and action == a
and args == arguments[0:len(args)]] #ignore extra trailing args
# print 'List ts %s' % ts # DEBUG
# Might be nondet. FSM: for now, just pick first transition that matches
current, (action,args,result), self.current = ts[0]
return result
def Current(self):
return self.current
def Restore(self, state):
self.current = state
# GetNext not needed