Skip to content

Commit 4b31d06

Browse files
committed
Add a script to generate state transition diagrams
1 parent 0c3a0f5 commit 4b31d06

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

generate_flow_diagram.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/python
2+
3+
import sys
4+
5+
NORMAL = "normal"
6+
FALLBACK = "fallback"
7+
8+
class Transition:
9+
def __init__(self, start, edge_name, end, style=NORMAL):
10+
self.start = start
11+
self.edge_name = edge_name
12+
self.end = end
13+
self.style = style
14+
15+
def extract_first_wrapped(table_string, key):
16+
try:
17+
start = table_string.index(key)
18+
except ValueError:
19+
return ""
20+
21+
depth = 0
22+
while depth == 0:
23+
start += 1
24+
if table_string[start] == "(":
25+
depth += 1
26+
27+
end = start + 1
28+
while depth != 0:
29+
end += 1
30+
if table_string[end] == "(":
31+
depth += 1
32+
if table_string[end] == ")":
33+
depth -= 1
34+
35+
state_string = table_string[start:end+1]
36+
37+
return state_string
38+
39+
40+
def extract_all_wrapped(table_string, key):
41+
states_strings = []
42+
end = 0
43+
while end < len(table_string):
44+
state_n = extract_first_wrapped(table_string[end:], key)
45+
if len(state_n) == 0:
46+
break
47+
states_strings.append(state_n)
48+
end += table_string[end:].index(state_n) + len(state_n)
49+
return states_strings
50+
51+
52+
def extract_transitions(file_string):
53+
table_string = extract_first_wrapped(file_string, "USM_TABLE")
54+
state_strings = extract_all_wrapped(table_string, "USM_STATE")
55+
error_state = table_string.split(",")[1].strip().split("::")[-1]
56+
transitions = []
57+
for state_string in state_strings:
58+
start_state = state_string.split(",")[1].strip().split("::")[-1]
59+
transition_strings = extract_all_wrapped(state_string, "USM_MAP")
60+
# print start_state, transition_strings
61+
state_transitions = []
62+
error_handled = False
63+
for transition_string in transition_strings:
64+
edge_name, end_state = (transition_string.replace("("," ").replace(")"," ").split(","))
65+
new_transition = Transition(start_state,
66+
edge_name.strip().split("::")[-1],
67+
end_state.strip().split("::")[-1])
68+
if new_transition.edge_name == "ERROR":
69+
error_handled = True
70+
state_transitions.append(new_transition)
71+
if not error_handled:
72+
for t in state_transitions:
73+
if t.start == start_state and t.end == error_state:
74+
t.edge_name += "\\nERROR"
75+
error_handled = True
76+
break
77+
if not error_handled:
78+
state_transitions.append(Transition(start_state, "ERROR", error_state, FALLBACK))
79+
transitions.extend(state_transitions)
80+
return transitions
81+
82+
def make_dot_file_string(transitions):
83+
output = []
84+
output.append("digraph {")
85+
for t in transitions:
86+
weight = 1
87+
if t.style == NORMAL:
88+
style = "solid"
89+
elif t.style == FALLBACK:
90+
style = "dotted"
91+
weight = 0.1
92+
else:
93+
style = "dashed"
94+
output.append(" \"{start}\" -> \"{end}\" [label=\"{name}\", "
95+
"style=\"{style}\", "
96+
"weight={weight}]".format(start=t.start,
97+
end=t.end,
98+
name=t.edge_name,
99+
style=style,
100+
weight=weight))
101+
output.append("}")
102+
return "\n".join(output)
103+
104+
105+
def help():
106+
print("Usage: generate_flow_diagram.py input_file [output_file]")
107+
108+
109+
def main():
110+
arguments = sys.argv[1:]
111+
if len(arguments) == 0:
112+
help()
113+
exit(1)
114+
115+
filename = arguments[0]
116+
117+
with open(filename, 'r') as file:
118+
cpp_string = file.read()
119+
120+
transitions = extract_transitions(cpp_string)
121+
dot_file_string = make_dot_file_string(transitions)
122+
123+
if len(arguments) == 2:
124+
out_filename = arguments[1]
125+
else:
126+
out_filename = filename + ".dot"
127+
128+
with open(out_filename, 'w') as output_file:
129+
output_file.write(dot_file_string)
130+
131+
if __name__ == "__main__":
132+
main()

test/usm_test.cpp.dot

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
digraph {
2+
"START" -> "PATH_A_1" [label="NEXT1", style="solid", weight=1]
3+
"START" -> "PATH_B_1" [label="NEXT2", style="solid", weight=1]
4+
"START" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
5+
"PATH_A_1" -> "PATH_A_2" [label="NEXT1", style="solid", weight=1]
6+
"PATH_A_1" -> "PATH_B_3" [label="ERROR", style="solid", weight=1]
7+
"PATH_A_2" -> "END" [label="NEXT1", style="solid", weight=1]
8+
"PATH_A_2" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
9+
"PATH_B_1" -> "PATH_B_2" [label="NEXT1", style="solid", weight=1]
10+
"PATH_B_1" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
11+
"PATH_B_2" -> "PATH_B_3" [label="NEXT1", style="solid", weight=1]
12+
"PATH_B_2" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
13+
"PATH_B_3" -> "END" [label="NEXT1", style="solid", weight=1]
14+
"PATH_B_3" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
15+
"CLEANUP" -> "END" [label="NEXT1", style="solid", weight=1]
16+
"CLEANUP" -> "CLEANUP" [label="ERROR", style="dotted", weight=0.1]
17+
}

0 commit comments

Comments
 (0)