1
2
3
4
5
6
7
8
9
10 import re
11 import copy
12 import rocon_utilities
13 import rosgraph.impl.graph
14 import roslib
15
16
17
18
19
21 if patternlist is None or len(patternlist) == 0:
22 return False
23 for pattern in patternlist:
24 if str(name).strip() == pattern:
25 return True
26 if re.match("^[a-zA-Z0-9_/]+$", pattern) is None:
27 if re.match(str(pattern), name.strip()) is not None:
28 return True
29 return False
30
31
33 - def __init__(self, incoming=None, outgoing=None):
34 self.incoming = incoming or []
35 self.outgoing = outgoing or []
36
37
39
42
43 - def _add_edge(self, edge, dotcode_factory, dotgraph, is_topic=False):
44 if is_topic:
45 dotcode_factory.add_edge_to_graph(dotgraph, edge.start, edge.end, label=edge.label, url='topic:%s' % edge.label)
46 else:
47 dotcode_factory.add_edge_to_graph(dotgraph, edge.start, edge.end, label=edge.label)
48
49 - def _add_node(self, node, rosgraphinst, dotcode_factory, dotgraph):
50 if node in rosgraphinst.bad_nodes:
51 bn = rosgraphinst.bad_nodes[node]
52 if bn.type == rosgraph.impl.graph.BadNode.DEAD:
53 dotcode_factory.add_node_to_graph(dotgraph,
54 nodename=node,
55 shape="doublecircle",
56 url=node,
57 color="red")
58 else:
59 dotcode_factory.add_node_to_graph(dotgraph,
60 nodename=node,
61 shape="doublecircle",
62 url=node,
63 color="orange")
64 else:
65 dotcode_factory.add_node_to_graph(dotgraph,
66 nodename=node,
67
68
69 shape='ellipse',
70 url=rocon_utilities.gateway_basename(node),
71
72 )
73
75 label = rosgraph.impl.graph.node_topic(node)
76 dotcode_factory.add_node_to_graph(dotgraph,
77 nodename=label,
78 nodelabel=label,
79 shape='box',
80 url="topic:%s" % label)
81
83 """
84 Determine the namespaces of the nodes being displayed
85 """
86 namespaces = []
87 nodes = graph.gateway_nodes
88 namespaces = list(set([roslib.names.namespace(n) for n in nodes]))
89 return list(set(namespaces))
90
92 nodenames = [str(n).strip() for n in nodes]
93
94 return [e for e in edges if e.start.strip() in nodenames and e.end.strip() in nodenames]
95
97 '''remove topic graphnodes without connected ROS nodes'''
98 removal_nodes = []
99 for n in connection_nodes:
100 keep = False
101 for e in edges:
102 if (e.start.strip() == str(n).strip() or e.end.strip() == str(n).strip()):
103 keep = True
104 break
105 if not keep:
106 removal_nodes.append(n)
107 for n in removal_nodes:
108 connection_nodes.remove(n)
109 return connection_nodes
110
112 '''splits a string after each comma, and treats tokens with leading dash as exclusions.
113 Adds .* as inclusion if no other inclusion option was given'''
114 includes = []
115 excludes = []
116 for name in ns_filter.split(','):
117 if name.strip().startswith('-'):
118 excludes.append(name.strip()[1:])
119 else:
120 includes.append(name.strip())
121 if includes == [] or includes == ['/'] or includes == ['']:
122 includes = ['.*']
123 return includes, excludes
124
126 '''returns a dict mapping node name to edge objects partitioned in incoming and outgoing edges'''
127 node_connections = {}
128 for edge in edges:
129 if not edge.start in node_connections:
130 node_connections[edge.start] = NodeConnections()
131 if not edge.end in node_connections:
132 node_connections[edge.end] = NodeConnections()
133 node_connections[edge.start].outgoing.append(edge)
134 node_connections[edge.end].incoming.append(edge)
135 return node_connections
136
137 - def _filter_leaves(self,
138 nodes_in,
139 edges_in,
140 node_connections,
141 hide_single_connection_topics,
142 hide_dead_end_topics):
143 '''
144 removes certain ending topic nodes and their edges from list of nodes and edges
145
146 @param hide_single_connection_topics: if true removes topics that are only published/subscribed by one node
147 @param hide_dead_end_topics: if true removes topics having only publishers
148 '''
149 if not hide_dead_end_topics and not hide_single_connection_topics:
150 return nodes_in, edges_in
151
152 nodes = copy.copy(nodes_in)
153 edges = copy.copy(edges_in)
154 removal_nodes = []
155 for n in nodes:
156 if n in node_connections:
157 node_edges = []
158 has_out_edges = False
159 node_edges.extend(node_connections[n].outgoing)
160 if len(node_connections[n].outgoing) > 0:
161 has_out_edges = True
162 node_edges.extend(node_connections[n].incoming)
163 if ((hide_single_connection_topics and len(node_edges) < 2) or
164 (hide_dead_end_topics and not has_out_edges)):
165 removal_nodes.append(n)
166 for e in node_edges:
167 if e in edges:
168 edges.remove(e)
169 for n in removal_nodes:
170 nodes.remove(n)
171 return nodes, edges
172
173 - def generate_dotgraph(self,
174 rosgraphinst,
175 ns_filter,
176 topic_filter,
177 dotcode_factory,
178 show_all_advertisements=False,
179 hide_dead_end_topics=False,
180 cluster_namespaces_level=0,
181 orientation='LR',
182 rank='same',
183 ranksep=0.2,
184 rankdir='TB',
185 simplify=True,
186 ):
187 """
188 See generate_dotcode
189 """
190 includes, excludes = self._split_filter_string(ns_filter)
191 connection_includes, connection_excludes = self._split_filter_string(topic_filter)
192
193 gateway_nodes = []
194 connection_nodes = []
195
196
197 gateway_nodes = rosgraphinst.gateway_nodes
198 gateway_nodes = [n for n in gateway_nodes if matches_any(n, includes) and not matches_any(n, excludes)]
199 edges = rosgraphinst.gateway_edges
200 edges = [e for e in edges if matches_any(e.label, connection_includes) and not matches_any(e.label, connection_excludes)]
201
202 hide_unused_advertisements = not show_all_advertisements
203 edges = self._filter_orphaned_edges(edges, list(gateway_nodes) + list(connection_nodes))
204 connection_nodes = self._filter_orphaned_topics(connection_nodes, edges)
205
206
207
208 dotgraph = dotcode_factory.get_graph(rank=rank,
209 ranksep=ranksep,
210 simplify=simplify,
211 rankdir=orientation)
212
213 namespace_clusters = {}
214
215 for n in (connection_nodes or []):
216
217 if (cluster_namespaces_level > 0 and
218 str(n).count('/') > 1 and
219 len(str(n).split('/')[1]) > 0):
220 namespace = str(n).split('/')[1]
221 if namespace not in namespace_clusters:
222 namespace_clusters[namespace] = dotcode_factory.add_subgraph_to_graph(dotgraph, namespace, rank=rank, rankdir=orientation, simplify=simplify)
223 self._add_topic_node(n, dotcode_factory=dotcode_factory, dotgraph=namespace_clusters[namespace])
224 else:
225 self._add_topic_node(n, dotcode_factory=dotcode_factory, dotgraph=dotgraph)
226
227
228
229 if gateway_nodes is not None:
230 for n in gateway_nodes:
231 if (cluster_namespaces_level > 0 and
232 str(n).count('/') >= 1 and
233 len(str(n).split('/')[1]) > 0 and
234 str(n).split('/')[1] in namespace_clusters):
235 namespace = str(n).split('/')[1]
236 self._add_node(n, rosgraphinst=rosgraphinst, dotcode_factory=dotcode_factory, dotgraph=namespace_clusters[namespace])
237 else:
238 self._add_node(n, rosgraphinst=rosgraphinst, dotcode_factory=dotcode_factory, dotgraph=dotgraph)
239
240 for e in edges:
241 self._add_edge(e, dotcode_factory, dotgraph=dotgraph)
242
243 return dotgraph
244
245 - def generate_dotcode(self,
246 rosgraphinst,
247 dotcode_factory,
248 ns_filter=' ',
249 topic_filter=' ',
250 show_all_advertisements=True,
251 hide_dead_end_topics=True,
252 cluster_namespaces_level=0,
253 orientation='LR',
254 rank='same',
255 ranksep=0.2,
256 rankdir='TB',
257 simplify=True,
258 ):
259 """
260 @param rosgraphinst: RosGraph instance
261 @param ns_filter: nodename filter
262 @type ns_filter: string
263 @param topic_filter: topicname filter
264 @type ns_filter: string
265 @param orientation: rankdir value (see ORIENTATIONS dict)
266 @type dotcode_factory: object
267 @param dotcode_factory: abstract factory manipulating dot language objects
268 @param hide_single_connection_topics: if true remove topics with just one connection
269 @param hide_dead_end_topics: if true remove topics with publishers only
270 @param cluster_namespaces_level: if > 0 places box around members of same namespace (TODO: multiple namespace layers)
271 @return: dotcode generated from graph singleton
272 @rtype: str
273 """
274 dotgraph = self.generate_dotgraph(rosgraphinst=rosgraphinst,
275 ns_filter=ns_filter,
276 topic_filter=topic_filter,
277 dotcode_factory=dotcode_factory,
278 show_all_advertisements=show_all_advertisements,
279 hide_dead_end_topics=hide_dead_end_topics,
280 cluster_namespaces_level=cluster_namespaces_level,
281 orientation=orientation,
282 rank=rank,
283 ranksep=ranksep,
284 rankdir=rankdir,
285 simplify=simplify,
286 )
287 dotcode = dotcode_factory.create_dot(dotgraph)
288 return dotcode
289