ssm_graphviz.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2015 Airbus
00004 # Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00005 #
00006 # Licensed under the Apache License, Version 2.0 (the "License");
00007 # you may not use this file except in compliance with the License.
00008 # You may obtain a copy of the License at
00009 #
00010 #   http://www.apache.org/licenses/LICENSE-2.0
00011 #
00012 # Unless required by applicable law or agreed to in writing, software
00013 # distributed under the License is distributed on an "AS IS" BASIS,
00014 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 # See the License for the specific language governing permissions and
00016 # limitations under the License.
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         # Construct proxies
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             # If this is also a container, recurse into it
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         #init
00148         node_name = 'init_'+ name
00149         if isinstance(state, smach.state_machine.StateMachine):  
00150             if(state._initial_state_label in self._parent_nodes): #transition from parent node
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             # If this is also a container, recurse into it
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]): #final
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): #transition to parent node
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): #transition from parent node
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)#,_edge.get_attributes())
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]): #final
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_)


airbus_ssm_core
Author(s): Ludovic Delval
autogenerated on Thu Jun 6 2019 17:59:28