1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 from __future__ import with_statement
36
37 import os
38 import itertools
39 import socket
40 import sys
41 import time
42 import xmlrpclib
43
44 import rospkg.environment
45
46 import rosgraph
47 import rosgraph.rosenv
48 import rosgraph.network
49
50 import rosnode
51 import rosservice
52
53 from roswtf.context import WtfException
54 from roswtf.environment import paths, is_executable
55 from roswtf.model import WtfWarning, WtfError
56 from roswtf.rules import warning_rule, error_rule
57
59
60 edges = []
61 for info in bus_info:
62
63 dest_id = info[1]
64 if dest_id.startswith('http://'):
65 if dest_id in ctx.uri_node_map:
66 dest_id = ctx.uri_node_map[dest_id]
67 else:
68 dest_id = 'unknown (%s)'%dest_id
69 direction = info[2]
70
71 topic = info[4]
72 if len(info) > 5:
73 connected = info[5]
74 else:
75 connected = True
76
77 if connected:
78 if direction == 'i':
79 edges.append((topic, dest_id, node))
80 elif direction == 'o':
81 edges.append((topic, node, dest_id))
82 elif direction == 'b':
83 print >> sys.stderr, "cannot handle bidirectional edges"
84 else:
85 raise Exception()
86
87 return edges
88
94
100
102 if not ctx.system_state or not ctx.nodes:
103 return
104 _, unpinged = rosnode.rosnode_ping_all()
105 return unpinged
106
108 if ctx.use_sim_time:
109 master = rosgraph.Master('/roswtf')
110 try:
111 pubtopics = master.getPublishedTopics('/')
112 except rosgraph.MasterException:
113 ctx.errors.append(WtfError("Cannot talk to ROS master"))
114 raise WtfException("roswtf lost connection to the ROS Master at %s"%rosgraph.rosenv.get_master_uri())
115
116 for topic, _ in pubtopics:
117 if topic in ['/time', '/clock']:
118 return
119 return True
120
121
123 master = rosgraph.Master('/roswtf')
124 errors = []
125 for service_name in ctx.services:
126 try:
127 service_uri = master.lookupService(service_name)
128 except:
129 ctx.errors.append(WtfError("cannot contact ROS Master at %s"%rosgraph.rosenv.get_master_uri()))
130 raise WtfException("roswtf lost connection to the ROS Master at %s"%rosgraph.rosenv.get_master_uri())
131
132 try:
133 headers = rosservice.get_service_headers(service_name, service_uri)
134 if not headers:
135 errors.append("service [%s] did not return service headers"%service_name)
136 except rosgraph.network.ROSHandshakeException as e:
137 errors.append("service [%s] appears to be malfunctioning"%service_name)
138 except Exception as e:
139 errors.append("service [%s] appears to be malfunctioning: %s"%(service_name, e))
140 return errors
141
143 ret = ''
144 whitelist = ['/reset_time']
145 if ctx.use_sim_time:
146 for sub, l in ctx.unconnected_subscriptions.iteritems():
147 l = [t for t in l if t not in whitelist]
148 if l:
149 ret += ' * %s:\n'%sub
150 ret += ''.join([" * %s\n"%t for t in l])
151 else:
152 for sub, l in ctx.unconnected_subscriptions.iteritems():
153 l = [t for t in l if t not in ['/time', '/clock']]
154 if l:
155 ret += ' * %s:\n'%sub
156 ret += ''.join([" * %s\n"%t for t in l])
157 return ret
158
159 graph_warnings = [
160 (unconnected_subscriptions, "The following node subscriptions are unconnected:\n"),
161 (unexpected_edges, "The following nodes are unexpectedly connected:"),
162 ]
163
164 graph_errors = [
165 (simtime_check, "/use_simtime is set but no publisher of /clock is present"),
166 (ping_check, "Could not contact the following nodes:"),
167 (missing_edges, "The following nodes should be connected but aren't:"),
168 (probe_all_services, "Errors connecting to the following services:"),
169 ]
170
172
173
174 if 0:
175 rospy.Subscriber(t, msg_class)
176
177
178
179
180
181
182
183
184 service_errors = [
185 ]
186 service_warnings = [
187 ]
188 topic_errors = [
189 (topic_timestamp_drift, "Timestamp drift:")
190 ]
191 topic_warnings = [
192 ]
193 node_errors = [
194 ]
195 node_warnings = [
196 ]
197
198
200 param_server = rosgraph.Master('/roswtf')
201 ctx.use_sim_time = False
202 try:
203 val = simtime = param_server.getParam('/use_sim_time')
204 if val:
205 ctx.use_sim_time = True
206 except:
207 pass
208
210 socket.setdefaulttimeout(3.0)
211 master = rosgraph.Master('/roswtf')
212
213
214 try:
215 val = master.getSystemState()
216 except rosgraph.MasterException:
217 return
218 ctx.system_state = val
219
220 pubs, subs, srvs = val
221
222
223 topics = []
224 for t, _ in itertools.chain(pubs, subs):
225 topics.append(t)
226 services = []
227 service_providers = []
228 for s, l in srvs:
229 services.append(s)
230 service_providers.extend(l)
231 ctx.topics = topics
232 ctx.services = services
233 ctx.service_providers = service_providers
234
235
236 nodes = []
237 for s in val:
238 for t, l in s:
239 nodes.extend(l)
240 ctx.nodes = list(set(nodes))
241
242
243 count = 0
244 start = time.time()
245 for n in ctx.nodes:
246 count += 1
247 try:
248 val = master.lookupNode(n)
249 except socket.error:
250 ctx.errors.append(WtfError("cannot contact ROS Master at %s"%rosgraph.rosenv.get_master_uri()))
251 raise WtfException("roswtf lost connection to the ROS Master at %s"%rosgraph.rosenv.get_master_uri())
252 ctx.uri_node_map[val] = n
253 end = time.time()
254
255 if count:
256 if ((end - start) / count) > 1.:
257 ctx.warnings.append(WtfError("Communication with master is very slow (>1s average)"))
258 elif (end - start) / count > .5:
259 ctx.warnings.append(WtfWarning("Communication with master is very slow (>0.5s average)"))
260
261 import threading
263 - def __init__(self, n, ctx, master, actual_edges, lock):
264 threading.Thread.__init__(self)
265 self.master = master
266 self.actual_edges = actual_edges
267 self.lock = lock
268 self.n = n
269 self.done = False
270 self.ctx = ctx
271
273 ctx = self.ctx
274 master = self.master
275 actual_edges = self.actual_edges
276 lock = self.lock
277 n = self.n
278
279 try:
280 socket.setdefaulttimeout(3.0)
281 with lock:
282 node_api = rosnode.get_api_uri(master, n)
283
284
285 if not node_api:
286 with lock:
287 ctx.errors.append(WtfError("Master does not have lookup information for node [%s]"%n))
288 return
289
290 node = xmlrpclib.ServerProxy(node_api)
291 start = time.time()
292 socket.setdefaulttimeout(3.0)
293 code, msg, bus_info = node.getBusInfo('/roswtf')
294 end = time.time()
295 with lock:
296 if (end-start) > 1.:
297 ctx.warnings.append(WtfWarning("Communication with node [%s] is very slow"%n))
298 if code != 1:
299 ctx.warnings.append(WtfWarning("Node [%s] would not return bus info"%n))
300 elif not bus_info:
301 if not n in ctx.service_providers:
302 ctx.warnings.append(WtfWarning("Node [%s] is not connected to anything"%n))
303 else:
304 edges = _businfo(ctx, n, bus_info)
305 actual_edges.extend(edges)
306 except socket.error, e:
307 pass
308 except Exception, e:
309 ctx.errors.append(WtfError("Communication with [%s] raised an error: %s"%(n, str(e))))
310 finally:
311 self.done = True
312
313
314
315
317 socket.setdefaulttimeout(3.0)
318 master = rosgraph.Master('/roswtf')
319
320
321 pubs, subs, _ = ctx.system_state
322 expected_edges = []
323 unconnected_subscriptions = {}
324
325
326 pub_dict = {}
327 for t, pub_list in pubs:
328 pub_dict[t] = pub_list
329
330 for t, sub_list in subs:
331 for sub in sub_list:
332 if t in pub_dict:
333 expected_edges.extend([(t, pub, sub) for pub in pub_dict[t]])
334 elif sub in unconnected_subscriptions:
335 unconnected_subscriptions[sub].append(t)
336 else:
337 unconnected_subscriptions[sub] = [t]
338
339
340 actual_edges = []
341 lock = threading.Lock()
342 threads = []
343 for n in ctx.nodes:
344 t =NodeInfoThread(n, ctx, master, actual_edges, lock)
345 threads.append(t)
346 t.start()
347
348
349
350 timeout_t = time.time() + 60.0
351 while time.time() < timeout_t and [t for t in threads if not t.done]:
352 time.sleep(0.5)
353
354 ctx.expected_edges = expected_edges
355 ctx.actual_edges = actual_edges
356 ctx.unconnected_subscriptions = unconnected_subscriptions
357
359
360 _compute_sim_time(ctx)
361 _compute_system_state(ctx)
362 _compute_connectivity(ctx)
363
365 master_uri = ctx.ros_master_uri
366
367
368
369
370
371 master = rosgraph.Master('/roswtf')
372 try:
373 master.getPid()
374 except rospkg.MasterException:
375 warning_rule((True, "Cannot communicate with master, ignoring online checks"), True, ctx)
376 return
377
378
379 print "analyzing graph..."
380 _compute_online_context(ctx)
381 print "... done analyzing graph"
382
383 if names:
384 check_topics = [t for t in names if t in ctx.topics]
385 check_services = [t for t in names if t in ctx.services]
386 check_nodes = [t for t in names if t in ctx.nodes]
387 unknown = [t for t in names if t not in check_topics + check_services + check_nodes]
388 if unknown:
389 raise WtfException("The following names were not found in the list of nodes, topics, or services:\n%s"%(''.join([" * %s\n"%t for t in unknown])))
390
391 for t in check_topics:
392 for r in topic_warnings:
393 warning_rule(r, r[0](ctx, t), ctx)
394 for r in topic_errors:
395 error_rule(r, r[0](ctx, t), ctx)
396 for s in check_services:
397 for r in service_warnings:
398 warning_rule(r, r[0](ctx, s), ctx)
399 for r in service_errors:
400 error_rule(r, r[0](ctx, s), ctx)
401 for n in check_nodes:
402 for r in node_warnings:
403 warning_rule(r, r[0](ctx, n), ctx)
404 for r in node_errors:
405 error_rule(r, r[0](ctx, n), ctx)
406
407
408 print "running graph rules..."
409 for r in graph_warnings:
410 warning_rule(r, r[0](ctx), ctx)
411 for r in graph_errors:
412 error_rule(r, r[0](ctx), ctx)
413 print "... done running graph rules"
414