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 ROS Master API.
36
37 L{ROSMasterHandler} provides the API implementation of the
38 Master. Python allows an API to be introspected from a Python class,
39 so the handler has a 1-to-1 mapping with the actual XMLRPC API.
40
41 API return convention: (statusCode, statusMessage, returnValue)
42
43 - statusCode: an integer indicating the completion condition of the method.
44 - statusMessage: a human-readable string message for debugging
45 - returnValue: the return value of the method; method-specific.
46
47 Current status codes:
48
49 - -1: ERROR: Error on the part of the caller, e.g. an invalid parameter
50 - 0: FAILURE: Method was attempted but failed to complete correctly.
51 - 1: SUCCESS: Method completed successfully.
52
53 Individual methods may assign additional meaning/semantics to statusCode.
54 """
55
56 import os
57 import sys
58 import logging
59 import threading
60 import time
61 import traceback
62
63 from roslib.xmlrpc import XmlRpcHandler
64
65 import roslib.names
66 from roslib.names import resolve_name
67 import rosmaster.paramserver
68 import rosmaster.threadpool
69
70 from rosmaster.util import xmlrpcapi
71 from rosmaster.registrations import RegistrationManager
72 from rosmaster.validators import non_empty, non_empty_str, not_none, is_api, is_topic, is_service, valid_type_name, valid_name, empty_or_valid_name, ParameterInvalid
73
74 NUM_WORKERS = 3
75
76
77 STATUS = 0
78 MSG = 1
79 VAL = 2
80
81 _logger = logging.getLogger("rosmaster.master")
82
83 LOG_API = False
86 """
87 Info-level master log statements. These statements may be printed
88 to screen so they should be user-readable.
89 @param msg: Message string
90 @type msg: str
91 @param args: arguments for msg if msg is a format string
92 """
93
94 _logger.info(msg, *args)
95
97 """
98 Warn-level master log statements. These statements may be printed
99 to screen so they should be user-readable.
100 @param msg: Message string
101 @type msg: str
102 @param args: arguments for msg if msg is a format string
103 """
104
105 _logger.warn(msg, *args)
106 if args:
107 print "WARN: "+msg%args
108 else:
109 print "WARN: "+str(msg)
110
111
112 -def apivalidate(error_return_value, validators=()):
113 """
114 ROS master/slave arg-checking decorator. Applies the specified
115 validator to the corresponding argument and also remaps each
116 argument to be the value returned by the validator. Thus,
117 arguments can be simultaneously validated and canonicalized prior
118 to actual function call.
119 @param error_return_value: API value to return if call unexpectedly fails
120 @param validators: sequence of validators to apply to each
121 arg. None means no validation for the parameter is required. As all
122 api methods take caller_id as the first parameter, the validators
123 start with the second param.
124 @type validators: sequence
125 """
126 def check_validates(f):
127 assert len(validators) == f.func_code.co_argcount - 2, "%s failed arg check"%f
128 def validated_f(*args, **kwds):
129 if LOG_API:
130 _logger.debug("%s%s", f.func_name, str(args[1:]))
131
132 if len(args) == 1:
133 _logger.error("%s invoked without caller_id paramter"%f.func_name)
134 return -1, "missing required caller_id parameter", error_return_value
135 elif len(args) != f.func_code.co_argcount:
136 return -1, "Error: bad call arity", error_return_value
137
138 instance = args[0]
139 caller_id = args[1]
140 if not isinstance(caller_id, basestring):
141 _logger.error("%s: invalid caller_id param type", f.func_name)
142 return -1, "caller_id must be a string", error_return_value
143
144 newArgs = [instance, caller_id]
145 try:
146 for (v, a) in zip(validators, args[2:]):
147 if v:
148 try:
149 newArgs.append(v(a, caller_id))
150 except ParameterInvalid, e:
151 _logger.error("%s: invalid parameter: %s", f.func_name, str(e) or 'error')
152 return -1, str(e) or 'error', error_return_value
153 else:
154 newArgs.append(a)
155
156 if LOG_API:
157 retval = f(*newArgs, **kwds)
158 _logger.debug("%s%s returns %s", f.func_name, args[1:], retval)
159 return retval
160 else:
161 code, msg, val = f(*newArgs, **kwds)
162 if val is None:
163 return -1, "Internal error (None value returned)", error_return_value
164 return code, msg, val
165 except TypeError, te:
166 _logger.error(traceback.format_exc())
167 return -1, "Error: invalid arguments: %s"%te, error_return_value
168 except Exception, e:
169 _logger.error(traceback.format_exc())
170 return 0, "Internal failure: %s"%e, error_return_value
171 validated_f.func_name = f.func_name
172 validated_f.__doc__ = f.__doc__
173 return validated_f
174 return check_validates
175
177 """
178 Contact api.publisherUpdate with specified parameters
179 @param api: XML-RPC URI of node to contact
180 @type api: str
181 @param topic: Topic name to send to node
182 @type topic: str
183 @param pub_uris: list of publisher APIs to send to node
184 @type pub_uris: [str]
185 """
186
187 mloginfo("publisherUpdate[%s] -> %s", topic, api)
188
189 xmlrpcapi(api).publisherUpdate('/master', topic, pub_uris)
190
192 """
193 Contact api.serviceUpdate with specified parameters
194 @param api: XML-RPC URI of node to contact
195 @type api: str
196 @param service: Service name to send to node
197 @type service: str
198 @param uri: URI to send to node
199 @type uri: str
200 """
201 mloginfo("serviceUpdate[%s, %s] -> %s",service, uri, api)
202 xmlrpcapi(api).serviceUpdate('/master', service, uri)
203
208 """
209 XML-RPC handler for ROS master APIs.
210 API routines for the ROS Master Node. The Master Node is a
211 superset of the Slave Node and contains additional API methods for
212 creating and monitoring a graph of slave nodes.
213
214 By convention, ROS nodes take in caller_id as the first parameter
215 of any API call. The setting of this parameter is rarely done by
216 client code as ros::msproxy::MasterProxy automatically inserts
217 this parameter (see ros::client::getMaster()).
218 """
219
221 """ctor."""
222
223 self.uri = None
224 self.done = False
225
226 self.thread_pool = rosmaster.threadpool.MarkedThreadPool(NUM_WORKERS)
227
228 self.ps_lock = threading.Condition(threading.Lock())
229
230 self.reg_manager = RegistrationManager(self.thread_pool)
231
232
233 self.publishers = self.reg_manager.publishers
234 self.subscribers = self.reg_manager.subscribers
235 self.services = self.reg_manager.services
236 self.param_subscribers = self.reg_manager.param_subscribers
237
238 self.topics_types = {}
239
240
241 self.param_server = rosmaster.paramserver.ParamDictionary(self.reg_manager)
242
244 if self.thread_pool is not None:
245 self.thread_pool.join_all(wait_for_tasks=False, wait_for_threads=False)
246 self.thread_pool = None
247 self.done = True
248
250 """
251 Initialize the handler with the XMLRPC URI. This is a standard callback from the roslib.xmlrpc.XmlRpcNode API.
252
253 @param uri: XML-RPC URI
254 @type uri: str
255 """
256 self.uri = uri
257
260
261
262
263
264 @apivalidate(0, (None, ))
266 """
267 Stop this server
268 @param caller_id: ROS caller id
269 @type caller_id: str
270 @param msg: a message describing why the node is being shutdown.
271 @type msg: str
272 @return: [code, msg, 0]
273 @rtype: [int, str, int]
274 """
275 if msg:
276 print >> sys.stdout, "shutdown request: %s"%msg
277 else:
278 print >> sys.stdout, "shutdown requst"
279 self._shutdown('external shutdown request from [%s]: %s'%(caller_id, msg))
280 return 1, "shutdown", 0
281
282 @apivalidate('')
284 """
285 Get the XML-RPC URI of this server.
286 @param caller_id str: ROS caller id
287 @return [int, str, str]: [1, "", xmlRpcUri]
288 """
289 return 1, "", self.uri
290
291
292 @apivalidate(-1)
294 """
295 Get the PID of this server
296 @param caller_id: ROS caller id
297 @type caller_id: str
298 @return: [1, "", serverProcessPID]
299 @rtype: [int, str, int]
300 """
301 return 1, "", os.getpid()
302
303
304
305
306
307 @apivalidate(0, (non_empty_str('key'),))
309 """
310 Parameter Server: delete parameter
311 @param caller_id: ROS caller id
312 @type caller_id: str
313 @param key: parameter name
314 @type key: str
315 @return: [code, msg, 0]
316 @rtype: [int, str, int]
317 """
318 try:
319 key = resolve_name(key, caller_id)
320 self.param_server.delete_param(key, self._notify_param_subscribers)
321 mloginfo("-PARAM [%s] by %s",key, caller_id)
322 return 1, "parameter %s deleted"%key, 0
323 except KeyError, e:
324 return -1, "parameter [%s] is not set"%key, 0
325
326 @apivalidate(0, (non_empty_str('key'), not_none('value')))
327 - def setParam(self, caller_id, key, value):
328 """
329 Parameter Server: set parameter. NOTE: if value is a
330 dictionary it will be treated as a parameter tree, where key
331 is the parameter namespace. For example:::
332 {'x':1,'y':2,'sub':{'z':3}}
333
334 will set key/x=1, key/y=2, and key/sub/z=3. Furthermore, it
335 will replace all existing parameters in the key parameter
336 namespace with the parameters in value. You must set
337 parameters individually if you wish to perform a union update.
338
339 @param caller_id: ROS caller id
340 @type caller_id: str
341 @param key: parameter name
342 @type key: str
343 @param value: parameter value.
344 @type value: XMLRPCLegalValue
345 @return: [code, msg, 0]
346 @rtype: [int, str, int]
347 """
348 key = resolve_name(key, caller_id)
349 self.param_server.set_param(key, value, self._notify_param_subscribers)
350 mloginfo("+PARAM [%s] by %s",key, caller_id)
351 return 1, "parameter %s set"%key, 0
352
353 @apivalidate(0, (non_empty_str('key'),))
355 """
356 Retrieve parameter value from server.
357 @param caller_id: ROS caller id
358 @type caller_id: str
359 @param key: parameter to lookup. If key is a namespace,
360 getParam() will return a parameter tree.
361 @type key: str
362 getParam() will return a parameter tree.
363
364 @return: [code, statusMessage, parameterValue]. If code is not
365 1, parameterValue should be ignored. If key is a namespace,
366 the return value will be a dictionary, where each key is a
367 parameter in that namespace. Sub-namespaces are also
368 represented as dictionaries.
369 @rtype: [int, str, XMLRPCLegalValue]
370 """
371 try:
372 key = resolve_name(key, caller_id)
373 return 1, "Parameter [%s]"%key, self.param_server.get_param(key)
374 except KeyError, e:
375 return -1, "Parameter [%s] is not set"%key, 0
376
377 @apivalidate(0, (non_empty_str('key'),))
379 """
380 Search for parameter key on parameter server. Search starts in caller's namespace and proceeds
381 upwards through parent namespaces until Parameter Server finds a matching key.
382
383 searchParam's behavior is to search for the first partial match.
384 For example, imagine that there are two 'robot_description' parameters::
385
386 /robot_description
387 /robot_description/arm
388 /robot_description/base
389 /pr2/robot_description
390 /pr2/robot_description/base
391
392 If I start in the namespace /pr2/foo and search for
393 'robot_description', searchParam will match
394 /pr2/robot_description. If I search for 'robot_description/arm'
395 it will return /pr2/robot_description/arm, even though that
396 parameter does not exist (yet).
397
398 @param caller_id str: ROS caller id
399 @type caller_id: str
400 @param key: parameter key to search for.
401 @type key: str
402 @return: [code, statusMessage, foundKey]. If code is not 1, foundKey should be
403 ignored.
404 @rtype: [int, str, str]
405 """
406 search_key = self.param_server.search_param(caller_id, key)
407 if search_key:
408 return 1, "Found [%s]"%search_key, search_key
409 else:
410 return -1, "Cannot find parameter [%s] in an upwards search"%key, ''
411
412 @apivalidate(0, (is_api('caller_api'), non_empty_str('key'),))
414 """
415 Retrieve parameter value from server and subscribe to updates to that param. See
416 paramUpdate() in the Node API.
417 @param caller_id str: ROS caller id
418 @type caller_id: str
419 @param key: parameter to lookup.
420 @type key: str
421 @param caller_api: API URI for paramUpdate callbacks.
422 @type caller_api: str
423 @return: [code, statusMessage, parameterValue]. If code is not
424 1, parameterValue should be ignored. parameterValue is an empty dictionary if the parameter
425 has not been set yet.
426 @rtype: [int, str, XMLRPCLegalValue]
427 """
428 key = resolve_name(key, caller_id)
429 try:
430
431
432 self.ps_lock.acquire()
433 val = self.param_server.subscribe_param(key, (caller_id, caller_api))
434 finally:
435 self.ps_lock.release()
436 return 1, "Subscribed to parameter [%s]"%key, val
437
438 @apivalidate(0, (is_api('caller_api'), non_empty_str('key'),))
440 """
441 Retrieve parameter value from server and subscribe to updates to that param. See
442 paramUpdate() in the Node API.
443 @param caller_id str: ROS caller id
444 @type caller_id: str
445 @param key: parameter to lookup.
446 @type key: str
447 @param caller_api: API URI for paramUpdate callbacks.
448 @type caller_api: str
449 @return: [code, statusMessage, numUnsubscribed].
450 If numUnsubscribed is zero it means that the caller was not subscribed to the parameter.
451 @rtype: [int, str, int]
452 """
453 key = resolve_name(key, caller_id)
454 try:
455
456 self.ps_lock.acquire()
457 retval = self.param_server.unsubscribe_param(key, (caller_id, caller_api))
458 finally:
459 self.ps_lock.release()
460 return 1, "Unsubscribe to parameter [%s]"%key, 1
461
462
463 @apivalidate(False, (non_empty_str('key'),))
465 """
466 Check if parameter is stored on server.
467 @param caller_id str: ROS caller id
468 @type caller_id: str
469 @param key: parameter to check
470 @type key: str
471 @return: [code, statusMessage, hasParam]
472 @rtype: [int, str, bool]
473 """
474 key = resolve_name(key, caller_id)
475 if self.param_server.has_param(key):
476 return 1, key, True
477 else:
478 return 1, key, False
479
480 @apivalidate([])
482 """
483 Get list of all parameter names stored on this server.
484 This does not adjust parameter names for caller's scope.
485
486 @param caller_id: ROS caller id
487 @type caller_id: str
488 @return: [code, statusMessage, parameterNameList]
489 @rtype: [int, str, [str]]
490 """
491 return 1, "Parameter names", self.param_server.get_param_names()
492
493
494
495
496 - def _notify(self, registrations, task, key, value):
497 """
498 Generic implementation of callback notification
499 @param registrations: Registrations
500 @type registrations: L{Registrations}
501 @param task: task to queue
502 @type task: fn
503 @param key: registration key
504 @type key: str
505 @param value: value to pass to task
506 @type value: Any
507 """
508
509 thread_pool = self.thread_pool
510 if not thread_pool:
511 return
512
513 if registrations.has_key(key):
514 try:
515 for node_api in registrations.get_apis(key):
516
517 thread_pool.queue_task(node_api, task, (node_api, key, value))
518 except KeyError:
519 _logger.warn('subscriber data stale (key [%s], listener [%s]): node API unknown'%(key, s))
520
522 """
523 Notify parameter subscribers of new parameter value
524 @param updates [([str], str, any)*]: [(subscribers, param_key, param_value)*]
525 @param param_value str: parameter value
526 """
527
528 thread_pool = self.thread_pool
529 if not thread_pool:
530 return
531
532 for subscribers, key, value in updates:
533
534 for caller_id, caller_api in subscribers:
535 self.thread_pool.queue_task(caller_api, self.param_update_task, (caller_id, caller_api, key, value))
536
538 """
539 Contact api.paramUpdate with specified parameters
540 @param caller_id: caller ID
541 @type caller_id: str
542 @param caller_api: XML-RPC URI of node to contact
543 @type caller_api: str
544 @param param_key: parameter key to pass to node
545 @type param_key: str
546 @param param_value: parameter value to pass to node
547 @type param_value: str
548 """
549 mloginfo("paramUpdate[%s]", param_key)
550 code, _, _ = xmlrpcapi(caller_api).paramUpdate('/master', param_key, param_value)
551 if code == -1:
552 try:
553
554 self.ps_lock.acquire()
555
556 matches = self.reg_manager.reverse_lookup(caller_api)
557 for m in matches:
558 retval = self.param_server.unsubscribe_param(param_key, (m.id, caller_api))
559 finally:
560 self.ps_lock.release()
561
562
564 """
565 Notify subscribers with new publisher list
566 @param topic: name of topic
567 @type topic: str
568 @param pub_uris: list of URIs of publishers.
569 @type pub_uris: [str]
570 """
571 self._notify(self.subscribers, publisher_update_task, topic, pub_uris)
572
574 """
575 Notify clients of new service provider
576 @param service: name of service
577 @type service: str
578 @param service_api: new service URI
579 @type service_api: str
580 """
581
582 self._notify(self.service_clients, service_update_task, service, service_api)
583
584
585
586
587 @apivalidate(0, ( is_service('service'), is_api('service_api'), is_api('caller_api')))
589 """
590 Register the caller as a provider of the specified service.
591 @param caller_id str: ROS caller id
592 @type caller_id: str
593 @param service: Fully-qualified name of service
594 @type service: str
595 @param service_api: Service URI
596 @type service_api: str
597 @param caller_api: XML-RPC URI of caller node
598 @type caller_api: str
599 @return: (code, message, ignore)
600 @rtype: (int, str, int)
601 """
602 try:
603 self.ps_lock.acquire()
604 self.reg_manager.register_service(service, caller_id, caller_api, service_api)
605 mloginfo("+SERVICE [%s] %s %s", service, caller_id, caller_api)
606 if 0:
607 self._notify_service_update(service, service_api)
608 finally:
609 self.ps_lock.release()
610 return 1, "Registered [%s] as provider of [%s]"%(caller_id, service), 1
611
612 @apivalidate(0, (is_service('service'),))
614 """
615 Lookup all provider of a particular service.
616 @param caller_id str: ROS caller id
617 @type caller_id: str
618 @param service: fully-qualified name of service to lookup.
619 @type: service: str
620 @return: (code, message, serviceUrl). service URL is provider's
621 ROSRPC URI with address and port. Fails if there is no provider.
622 @rtype: (int, str, str)
623 """
624 try:
625 self.ps_lock.acquire()
626 service_url = self.services.get_service_api(service)
627 finally:
628 self.ps_lock.release()
629 if service_url:
630 return 1, "rosrpc URI: [%s]"%service_url, service_url
631 else:
632 return -1, "no provider", ''
633
634 @apivalidate(0, ( is_service('service'), is_api('service_api')))
636 """
637 Unregister the caller as a provider of the specified service.
638 @param caller_id str: ROS caller id
639 @type caller_id: str
640 @param service: Fully-qualified name of service
641 @type service: str
642 @param service_api: API URI of service to unregister. Unregistration will only occur if current
643 registration matches.
644 @type service_api: str
645 @return: (code, message, numUnregistered). Number of unregistrations (either 0 or 1).
646 If this is zero it means that the caller was not registered as a service provider.
647 The call still succeeds as the intended final state is reached.
648 @rtype: (int, str, int)
649 """
650 try:
651 self.ps_lock.acquire()
652 retval = self.reg_manager.unregister_service(service, caller_id, service_api)
653 if 0:
654 self._notify_service_update(service, service_api)
655 mloginfo("-SERVICE [%s] %s %s", service, caller_id, service_api)
656 return retval
657 finally:
658 self.ps_lock.release()
659
660
661
662
663 @apivalidate(0, ( is_topic('topic'), valid_type_name('topic_type'), is_api('caller_api')))
665 """
666 Subscribe the caller to the specified topic. In addition to receiving
667 a list of current publishers, the subscriber will also receive notifications
668 of new publishers via the publisherUpdate API.
669 @param caller_id: ROS caller id
670 @type caller_id: str
671 @param topic str: Fully-qualified name of topic to subscribe to.
672 @param topic_type: Datatype for topic. Must be a package-resource name, i.e. the .msg name.
673 @type topic_type: str
674 @param caller_api: XML-RPC URI of caller node for new publisher notifications
675 @type caller_api: str
676 @return: (code, message, publishers). Publishers is a list of XMLRPC API URIs
677 for nodes currently publishing the specified topic.
678 @rtype: (int, str, [str])
679 """
680
681 try:
682 self.ps_lock.acquire()
683 self.reg_manager.register_subscriber(topic, caller_id, caller_api)
684
685
686
687 if not topic in self.topics_types and topic_type != roslib.names.ANYTYPE:
688 self.topics_types[topic] = topic_type
689
690 mloginfo("+SUB [%s] %s %s",topic, caller_id, caller_api)
691 pub_uris = self.publishers.get_apis(topic)
692 finally:
693 self.ps_lock.release()
694 return 1, "Subscribed to [%s]"%topic, pub_uris
695
696 @apivalidate(0, (is_topic('topic'), is_api('caller_api')))
698 """
699 Unregister the caller as a publisher of the topic.
700 @param caller_id: ROS caller id
701 @type caller_id: str
702 @param topic: Fully-qualified name of topic to unregister.
703 @type topic: str
704 @param caller_api: API URI of service to unregister. Unregistration will only occur if current
705 registration matches.
706 @type caller_api: str
707 @return: (code, statusMessage, numUnsubscribed).
708 If numUnsubscribed is zero it means that the caller was not registered as a subscriber.
709 The call still succeeds as the intended final state is reached.
710 @rtype: (int, str, int)
711 """
712 try:
713 self.ps_lock.acquire()
714 retval = self.reg_manager.unregister_subscriber(topic, caller_id, caller_api)
715 mloginfo("-SUB [%s] %s %s",topic, caller_id, caller_api)
716 return retval
717 finally:
718 self.ps_lock.release()
719
720 @apivalidate(0, ( is_topic('topic'), valid_type_name('topic_type'), is_api('caller_api')))
722 """
723 Register the caller as a publisher the topic.
724 @param caller_id: ROS caller id
725 @type caller_id: str
726 @param topic: Fully-qualified name of topic to register.
727 @type topic: str
728 @param topic_type: Datatype for topic. Must be a
729 package-resource name, i.e. the .msg name.
730 @type topic_type: str
731 @param caller_api str: ROS caller XML-RPC API URI
732 @type caller_api: str
733 @return: (code, statusMessage, subscriberApis).
734 List of current subscribers of topic in the form of XMLRPC URIs.
735 @rtype: (int, str, [str])
736 """
737
738 try:
739 self.ps_lock.acquire()
740 self.reg_manager.register_publisher(topic, caller_id, caller_api)
741
742 if topic_type != roslib.names.ANYTYPE or not topic in self.topics_types:
743 self.topics_types[topic] = topic_type
744 pub_uris = self.publishers.get_apis(topic)
745 self._notify_topic_subscribers(topic, pub_uris)
746 mloginfo("+PUB [%s] %s %s",topic, caller_id, caller_api)
747 sub_uris = self.subscribers.get_apis(topic)
748 finally:
749 self.ps_lock.release()
750 return 1, "Registered [%s] as publisher of [%s]"%(caller_id, topic), sub_uris
751
752
753 @apivalidate(0, (is_topic('topic'), is_api('caller_api')))
755 """
756 Unregister the caller as a publisher of the topic.
757 @param caller_id: ROS caller id
758 @type caller_id: str
759 @param topic: Fully-qualified name of topic to unregister.
760 @type topic: str
761 @param caller_api str: API URI of service to
762 unregister. Unregistration will only occur if current
763 registration matches.
764 @type caller_api: str
765 @return: (code, statusMessage, numUnregistered).
766 If numUnregistered is zero it means that the caller was not registered as a publisher.
767 The call still succeeds as the intended final state is reached.
768 @rtype: (int, str, int)
769 """
770 try:
771 self.ps_lock.acquire()
772 retval = self.reg_manager.unregister_publisher(topic, caller_id, caller_api)
773 if retval[VAL]:
774 self._notify_topic_subscribers(topic, self.publishers.get_apis(topic))
775 mloginfo("-PUB [%s] %s %s",topic, caller_id, caller_api)
776 finally:
777 self.ps_lock.release()
778 return retval
779
780
781
782
783 @apivalidate('', (valid_name('node'),))
785 """
786 Get the XML-RPC URI of the node with the associated
787 name/caller_id. This API is for looking information about
788 publishers and subscribers. Use lookupService instead to lookup
789 ROS-RPC URIs.
790 @param caller_id: ROS caller id
791 @type caller_id: str
792 @param node: name of node to lookup
793 @type node: str
794 @return: (code, msg, URI)
795 @rtype: (int, str, str)
796 """
797 try:
798 self.ps_lock.acquire()
799 node = self.reg_manager.get_node(node_name)
800 if node is not None:
801 retval = 1, "node api", node.api
802 else:
803 retval = -1, "unknown node [%s]"%node_name, ''
804 finally:
805 self.ps_lock.release()
806 return retval
807
808 @apivalidate(0, (empty_or_valid_name('subgraph'),))
810 """
811 Get list of topics that can be subscribed to. This does not return topics that have no publishers.
812 See L{getSystemState()} to get more comprehensive list.
813 @param caller_id: ROS caller id
814 @type caller_id: str
815 @param subgraph: Restrict topic names to match within the specified subgraph. Subgraph namespace
816 is resolved relative to the caller's namespace. Use '' to specify all names.
817 @type subgraph: str
818 @return: (code, msg, [[topic1, type1]...[topicN, typeN]])
819 @rtype: (int, str, [[str, str],])
820 """
821 try:
822 self.ps_lock.acquire()
823
824 if subgraph and subgraph[-1] != roslib.names.SEP:
825 subgraph = subgraph + roslib.names.SEP
826
827
828 retval = [[t, self.topics_types[t]] for t in self.publishers.iterkeys() if t.startswith(subgraph)]
829 finally:
830 self.ps_lock.release()
831 return 1, "current topics", retval
832
833 @apivalidate([])
835 """
836 Retrieve list topic names and their types.
837 @param caller_id: ROS caller id
838 @type caller_id: str
839 @rtype: (int, str, [[str,str]] )
840 @return: (code, statusMessage, topicTypes). topicTypes is a list of [topicName, topicType] pairs.
841 """
842 try:
843 self.ps_lock.acquire()
844 retval = self.topics_types.items()
845 finally:
846 self.ps_lock.release()
847 return 1, "current system state", retval
848
849 @apivalidate([[],[], []])
851 """
852 Retrieve list representation of system state (i.e. publishers, subscribers, and services).
853 @param caller_id: ROS caller id
854 @type caller_id: str
855 @rtype: (int, str, [[str,[str]], [str,[str]], [str,[str]]])
856 @return: (code, statusMessage, systemState).
857
858 System state is in list representation::
859 [publishers, subscribers, services].
860
861 publishers is of the form::
862 [ [topic1, [topic1Publisher1...topic1PublisherN]] ... ]
863
864 subscribers is of the form::
865 [ [topic1, [topic1Subscriber1...topic1SubscriberN]] ... ]
866
867 services is of the form::
868 [ [service1, [service1Provider1...service1ProviderN]] ... ]
869 """
870 edges = []
871 try:
872 self.ps_lock.acquire()
873 retval = [r.get_state() for r in (self.publishers, self.subscribers, self.services)]
874 finally:
875 self.ps_lock.release()
876 return 1, "current system state", retval
877