1+ import sys
2+ import re
3+ import networkx as nx
4+ from pathlib import Path
5+ import matplotlib .pyplot as plt
6+
7+ def get_states (manager_states_path ):
8+ file = Path (manager_states_path ).read_text (encoding = 'utf8' )
9+ states = re .findall (r'\s*(?:\w+)\s*\((?:[\s\S]*?)\)[,;]' , file , re .MULTILINE | re .IGNORECASE )
10+ return states
11+
12+ def parse_states (states ):
13+ parsed_states = []
14+ for state in states :
15+ state_name_match = re .match (r'^\s*(\w+)\s*\(' , state , re .IGNORECASE )
16+ sub_states = re .findall (r'\b(?:\w+\.)+\w+\b' , state )
17+ parsed_states .append ({
18+ 'stateName' : state_name_match [1 ] if state_name_match else None ,
19+ 'subStates' : sub_states
20+ })
21+ return parsed_states
22+
23+ def get_triggers (manager_path ):
24+ file = Path (manager_path ).read_text (encoding = 'utf8' )
25+ triggers = re .findall (r'addTrigger\(.+?\)' , file , re .MULTILINE | re .IGNORECASE )
26+ return triggers
27+
28+ def parse_triggers (triggers ):
29+ parsed_triggers = []
30+ for trigger in triggers :
31+ matched = re .search (r'\s*(\w+)\s*,\s*(\w+)\s*,\s*((?:[\w:]+)|(?:\(\)\s*->\s*[\w.]+\(\)))\s*\)' , trigger )
32+ if matched :
33+ parsed_triggers .append ({
34+ 'from' : matched [1 ],
35+ 'to' : matched [2 ],
36+ 'condition' : matched [3 ]
37+ })
38+ return parsed_triggers
39+
40+ def create_state_map (states , triggers ):
41+ state_map = {state ['stateName' ]: state for state in states }
42+
43+ for trigger in triggers :
44+ state = state_map .get (trigger ['from' ])
45+ if state is not None :
46+ if 'connectionsTo' not in state :
47+ state ['connectionsTo' ] = []
48+ state ['connectionsTo' ].append ({
49+ 'to' : trigger ['to' ],
50+ 'condition' : trigger ['condition' ]
51+ })
52+ return state_map
53+
54+ def generate_graph (state_map ):
55+ graph = nx .DiGraph ()
56+ for (state_name , state_info ) in state_map .items ():
57+ graph .add_node (state_name , label = state_name )
58+ for connection in state_info .get ('connectionsTo' , []):
59+ graph .add_edge (state_name , connection ['to' ], weight = 1 )
60+
61+
62+ pos = nx .shell_layout (graph )
63+ pos ["IDLE" ] = (0 , 0 )
64+
65+ fixed_nodes = ["IDLE" ]
66+ pos = nx .spring_layout (graph , pos = pos , fixed = fixed_nodes , k = 0.2 )
67+
68+ edge_labels = nx .get_edge_attributes (graph , 'label' )
69+
70+ nx .draw (graph , pos , with_labels = True , node_color = 'lightblue' , node_size = 1000 , font_size = 6 , arrows = True )
71+ nx .draw_networkx_edge_labels (graph , pos , edge_labels = edge_labels , font_size = 6 )
72+
73+ plt .title ("State Machine Visualization" )
74+ plt .tight_layout ()
75+ plt .savefig ("state_machine.png" , format = 'png' , dpi = 300 )
76+
77+
78+ def main (managerStatesPath , managerPath ):
79+ states = get_states (managerStatesPath )
80+ states = parse_states (states )
81+
82+ triggers = get_triggers (managerPath )
83+ triggers = parse_triggers (triggers )
84+
85+ state_map = create_state_map (states , triggers )
86+
87+ generate_graph (state_map )
88+
89+ print ("done" )
90+
91+ if __name__ == '__main__' :
92+ main (sys .argv [1 ], sys .argv [2 ])
0 commit comments