00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 from std_msgs.msg import String, Empty
00019
00020 import threading
00021 import smach
00022 import rospy
00023 import os
00024 import airbus_ssm_core.msg
00025 import pydot
00026 from ast import literal_eval
00027 from pydot import Subgraph
00028
00029
00030 class ssmGraph(object):
00031 def __init__(self, state_machine, action_server = None):
00032 """Traverse the smach tree starting at root, and construct introspection
00033 proxies for getting and setting debug state."""
00034 self._state_machine = state_machine
00035 self._graph_dot = pydot.Dot(graph_type = 'digraph', compound='true', newrank='true', minlen='0', size='"1,2"')
00036 self._level = 0
00037
00038 self._rank_nodes = {}
00039 self._parent_nodes = []
00040 self._parallel_nodes = []
00041 self._parallel_starting = []
00042 self._parallel_ending = []
00043 self._server_name = rospy.get_param('ssm_server_name', '/ssm')
00044 self._update_pub = rospy.Publisher(self._server_name + "/ssm_dotcode", String, queue_size = 1)
00045 self._sub_update = None
00046
00047
00048 def _update_cb(self, msg):
00049 statut_dict = literal_eval(msg.data)
00050 self.update_color('ROOT', statut_dict,self._graph_dot)
00051 self.send_graph()
00052
00053 def update_color(self, name, statut_dict, graph):
00054 for state_ in statut_dict[name]:
00055 if(state_ in self._parent_nodes):
00056 if(statut_dict[name][state_] == 1):
00057 glevel = 1
00058 for level in self._rank_nodes:
00059 if(state_ in self._rank_nodes[level]):
00060 glevel = int(level)
00061 if(glevel < 1):
00062 glevel = 1
00063 if(glevel > 4):
00064 glevel = 4
00065 color = 'lightblue'+str(glevel)
00066 elif(statut_dict[name][state_] == 0):
00067 color = 'white'
00068 else:
00069 color = 'yellow2'
00070
00071 if(len(graph.get_subgraph('cluster' + str(state_)))>0):
00072 graph.get_subgraph('cluster' + str(state_))[0].set('style','filled')
00073 graph.get_subgraph('cluster' + str(state_))[0].set('fillcolor',color)
00074 self.update_color(state_, statut_dict, graph.get_subgraph('cluster' + state_)[0])
00075 elif(len(graph.get_subgraph('"cluster' + state_+'"'))>0):
00076 graph.get_subgraph('"cluster' + state_+'"')[0].set('style','filled')
00077 graph.get_subgraph('"cluster' + state_+'"')[0].set('fillcolor',color)
00078 self.update_color(state_, statut_dict, graph.get_subgraph('"cluster' + state_+'"')[0])
00079 else:
00080 pass
00081
00082 else:
00083 if(statut_dict[name][state_] == 1):
00084 color = 'greenyellow'
00085 elif(statut_dict[name][state_] == 0):
00086 color = 'white'
00087 else:
00088 color = 'orange'
00089 if(len(graph.get_node(state_))>0):
00090 graph.get_node(state_)[0].set('style','filled')
00091 graph.get_node(state_)[0].set('fillcolor',color)
00092 elif(len(graph.get_node('"'+state_+'"'))>0):
00093 graph.get_node('"'+state_+'"')[0].set('style','filled')
00094 graph.get_node('"'+state_+'"')[0].set('fillcolor',color)
00095 else:
00096 pass
00097
00098
00099 def start(self):
00100
00101 self.construct_subgraph(self._state_machine, 'ROOT', self._graph_dot)
00102 self.layout_nodes(self._graph_dot)
00103 self.send_graph()
00104 self._sub_update = rospy.Subscriber(self._server_name + "/ssm_status", String, self._update_cb, queue_size = 1)
00105
00106 def send_graph(self):
00107 Msg = String()
00108 Msg.data = self._graph_dot.to_string()
00109 self._update_pub.publish(Msg)
00110
00111 def stop(self):
00112 if(self._sub_update is not None):
00113 self._sub_update.unregister()
00114
00115 def construct_subgraph(self, state, name, graph):
00116 """Recursively construct graph."""
00117 if isinstance(state, smach.concurrence.Concurrence):
00118 self.add_final(state, name, graph)
00119
00120 for (label, child) in state.get_children().items():
00121
00122 if(str(self._level) not in self._rank_nodes):
00123 self._rank_nodes[str(self._level)] = []
00124 self._rank_nodes[str(self._level)].append(label)
00125 if isinstance(child, smach.container.Container):
00126 self._parent_nodes.append(label)
00127 new_graph = pydot.Subgraph('cluster'+label, label=label)
00128 graph.add_subgraph(new_graph)
00129 if isinstance(child, smach.concurrence.Concurrence):
00130 self._parallel_nodes.append(label)
00131 self._level = self._level +1
00132 self.construct_subgraph(child, label, new_graph)
00133 self._level = self._level -1
00134 else:
00135 graph.add_node(pydot.Node(label, label=label))
00136
00137 self.add_initial(state, name, graph)
00138 if not(isinstance(state, smach.concurrence.Concurrence)):
00139 self.add_final(state, name, graph)
00140 self.create_edges(state, name, graph)
00141
00142
00143 def create_edges(self, state, name, graph):
00144 from_label = ''
00145 to_label = ''
00146 args_t = ''
00147
00148 node_name = 'init_'+ name
00149 if isinstance(state, smach.state_machine.StateMachine):
00150 if(state._initial_state_label in self._parent_nodes):
00151 _edge = pydot.Edge(node_name,'init_' + state._initial_state_label)
00152 _edge.set('lhead', 'cluster'+state._initial_state_label)
00153 graph.add_edge(_edge)
00154 else:
00155 graph.add_edge(pydot.Edge(node_name,state._initial_state_label))
00156 else:
00157 for (label, child) in state.get_children().items():
00158
00159 if isinstance(child, smach.container.Container):
00160 graph.add_edge(pydot.Edge(node_name,'init_' + label, style='invis'))
00161 else:
00162 graph.add_edge(pydot.Edge(node_name,label, style='invis'))
00163
00164 for transition in state.get_internal_edges():
00165 if(transition[0] != 'preempt'):
00166 _edge = pydot.Edge('""','""',label=transition[0])
00167 if(transition[0]==transition[2]):
00168 if(('"parrallel_'+name+'_'+transition[2]+'"') in self._parallel_ending):
00169 to_label = '"parrallel_'+name+'_'+transition[2]+'"'
00170 else:
00171 to_label = 'final_' + name+'_'+transition[0]
00172 else:
00173 if (transition[2] in self._parent_nodes):
00174 to_label = 'init_' + transition[2]
00175 _edge.set('lhead', 'cluster'+transition[2])
00176 else:
00177 if(transition[2] is None):
00178 to_label = transition[0]
00179 elif(('"parrallel_'+name+'_'+transition[2]+'"') in self._parallel_ending):
00180 to_label = '"parrallel_'+name+'_'+transition[2]+'"'
00181 else:
00182 to_label = transition[2]
00183
00184 if(transition[1] in self._parent_nodes):
00185 from_label = 'final_'+ transition[1] + '_'+transition[0]
00186 if(transition[1] in self._parallel_nodes):
00187 from_label = 'parrallel_'+transition[1]+'_'+transition[0]
00188 _edge.set('ltail', 'cluster'+transition[1])
00189 else:
00190 from_label = transition[1]
00191 new_edge = pydot.Edge(from_label,to_label)
00192 for attr_ in _edge.get_attributes():
00193 new_edge.set(attr_, _edge.get_attributes()[attr_])
00194 graph.add_edge(new_edge)
00195
00196 def add_final(self, state, state_label, graph):
00197 if(isinstance(state, smach.concurrence.Concurrence)):
00198 for transition in state.get_internal_edges():
00199 if(transition[0] != 'preempt'):
00200 node_name = 'parrallel_'+state_label+'_'+transition[2]
00201 self._parallel_ending.append('"'+node_name+'"')
00202 graph.add_node(pydot.Node(node_name,shape='underline',margin='0',height='0.2',label=transition[2]))
00203 else:
00204 for transition in state.get_internal_edges():
00205 if(transition[0] != 'preempt'):
00206 if(transition[0]==transition[2]):
00207 node_name = 'final_'+ state_label + '_'+transition[0]
00208 graph.add_node(pydot.Node(node_name,shape='doublecircle',style='filled',fillcolor='black',width='0.25',label='""'))
00209
00210 def add_initial(self, state, state_label, graph):
00211 node_name = 'init_'+ state_label
00212 if isinstance(state, smach.state_machine.StateMachine):
00213 graph.add_node(pydot.Node(node_name,shape='circle', style='filled',fillcolor='black',width='0.25',label='""'))
00214 else:
00215 graph.add_node(pydot.Node(node_name,shape='box',margin='0',width='0', height='0', fixedsize='true',style='invis',label='""'))
00216 self._parallel_starting = [node_name]
00217
00218 def layout_nodes(self, graph):
00219 subgraph_init = pydot.Subgraph(rank='same')
00220 for node in graph.get_node_list():
00221 if("init_" in node.get_name()):
00222 if(node.get_name() in self._parallel_starting):
00223 for edge_ in graph.get_edge_list():
00224 if(edge_.get_source()==node.get_name()):
00225 subgraph_init.add_node(pydot.Node(edge_.get_destination()))
00226 else:
00227 subgraph_init.add_node(node)
00228
00229
00230 subgraph_final = pydot.Subgraph(rank='same')
00231 for node in graph.get_node_list():
00232 if("final_" in node.get_name()):
00233 subgraph_final.add_node(node)
00234 if(node.get_name() in self._parallel_ending):
00235 subgraph_final.add_node(node)
00236 graph_list = graph.get_subgraph_list()
00237 graph.add_subgraph(subgraph_init)
00238 graph.add_subgraph(subgraph_final)
00239 for graph_ in graph_list:
00240 self.layout_nodes(graph_)