Package rosgraph :: Module masterapi
[frames] | no frames]

Source Code for Module rosgraph.masterapi

  1  # Software License Agreement (BSD License)
 
  2  #
 
  3  # Copyright (c) 2009, Willow Garage, Inc.
 
  4  # All rights reserved.
 
  5  #
 
  6  # Redistribution and use in source and binary forms, with or without
 
  7  # modification, are permitted provided that the following conditions
 
  8  # are met:
 
  9  #
 
 10  #  * Redistributions of source code must retain the above copyright
 
 11  #    notice, this list of conditions and the following disclaimer.
 
 12  #  * Redistributions in binary form must reproduce the above
 
 13  #    copyright notice, this list of conditions and the following
 
 14  #    disclaimer in the documentation and/or other materials provided
 
 15  #    with the distribution.
 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its
 
 17  #    contributors may be used to endorse or promote products derived
 
 18  #    from this software without specific prior written permission.
 
 19  #
 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 31  # POSSIBILITY OF SUCH DAMAGE.
 
 32  #
 
 33  # Revision $Id: masterapi.py 9672 2010-05-11 21:57:40Z kwc $
 
 34  """
 
 35  Python adapter for calling ROS Master API. While it is trivial to call the 
 
 36  Master directly using XML-RPC, this API provides a safer abstraction in the event
 
 37  the Master API is changed.
 
 38  """ 
 39  
 
 40  try: 
 41      from xmlrpc.client import ServerProxy  # Python 3.x 
 42  except ImportError: 
 43      from xmlrpclib import ServerProxy  # Python 2.x 
 44  
 
 45  from . names import make_caller_id 
 46  from . rosenv import get_master_uri 
 47  from . network import parse_http_host_and_port 
 48  
 
49 -class MasterException(Exception):
50 """ 51 Base class of ROS-master related errors. 52 """ 53 pass
54
55 -class MasterFailure(MasterException):
56 """ 57 Call to Master failed. This generally indicates an internal error 58 in the Master and that the Master may be in an inconsistent state. 59 """ 60 pass
61
62 -class MasterError(MasterException):
63 """ 64 Master returned an error code, which indicates an error in the 65 arguments passed to the Master. 66 """ 67 pass
68 69 # backwards compat 70 ROSMasterException = MasterException 71 Error = MasterError 72 Failure = MasterFailure 73
74 -def is_online(master_uri=None):
75 """ 76 @param master_uri: (optional) override environment's ROS_MASTER_URI 77 @type master_uri: str 78 @return: True if Master is available 79 """ 80 return Master('rosgraph', master_uri=master_uri).is_online()
81
82 -class Master(object):
83 """ 84 API for interacting with the ROS master. Although the Master is 85 relatively simple to interact with using the XMLRPC API, this 86 abstraction layer provides protection against future updates. It 87 also provides a streamlined API with builtin return code checking 88 and caller_id passing. 89 """ 90
91 - def __init__(self, caller_id, master_uri=None):
92 """ 93 :param caller_id: name of node to use in calls to master, ``str`` 94 :param master_uri: (optional) override default ROS master URI, ``str`` 95 :raises: :exc:`ValueError` If ROS master uri not set properly 96 """ 97 98 if master_uri is None: 99 master_uri = get_master_uri() 100 self._reinit(master_uri) 101 102 self.caller_id = make_caller_id(caller_id) #resolve 103 if self.caller_id[-1] == '/': 104 self.caller_id = self.caller_id[:-1]
105
106 - def _reinit(self, master_uri):
107 """ 108 Internal API for reinitializing this handle to be a new master 109 110 :raises: :exc:`ValueError` If ROS master uri not set 111 """ 112 if master_uri is None: 113 raise ValueError("ROS master URI is not set") 114 # #1730 validate URL for better error messages 115 try: 116 parse_http_host_and_port(master_uri) 117 except ValueError: 118 raise ValueError("invalid master URI: %s"%(master_uri)) 119 120 self.master_uri = master_uri 121 self.handle = ServerProxy(self.master_uri)
122
123 - def is_online(self):
124 """ 125 Check if Master is online. 126 127 NOTE: this is not part of the actual Master API. This is a convenience function. 128 129 @param master_uri: (optional) override environment's ROS_MASTER_URI 130 @type master_uri: str 131 @return: True if Master is available 132 """ 133 try: 134 self.getPid() 135 return True 136 except: 137 return False
138
139 - def _succeed(self, args):
140 """ 141 Check master return code and return the value field. 142 143 @param args: master return value 144 @type args: (int, str, XMLRPCLegalValue) 145 @return: value field of args (master return value) 146 @rtype: XMLRPCLegalValue 147 @raise rosgraph.masterapi.Error: if Master returns ERROR. 148 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 149 """ 150 code, msg, val = args 151 if code == 1: 152 return val 153 elif code == -1: 154 raise Error(msg) 155 else: 156 raise Failure(msg)
157 158 ################################################################################ 159 # PARAM SERVER 160
161 - def deleteParam(self, key):
162 """ 163 Parameter Server: delete parameter 164 @param key: parameter name 165 @type key: str 166 @return: 0 167 @rtype: int 168 """ 169 return self._succeed(self.handle.deleteParam(self.caller_id, key))
170
171 - def setParam(self, key, value):
172 """ 173 Parameter Server: set parameter. NOTE: if value is a 174 dictionary it will be treated as a parameter tree, where key 175 is the parameter namespace. For example::: 176 {'x':1,'y':2,'sub':{'z':3}} 177 178 will set key/x=1, key/y=2, and key/sub/z=3. Furthermore, it 179 will replace all existing parameters in the key parameter 180 namespace with the parameters in value. You must set 181 parameters individually if you wish to perform a union update. 182 183 @param key: parameter name 184 @type key: str 185 @param value: parameter value. 186 @type value: XMLRPCLegalValue 187 @return: 0 188 @rtype: int 189 """ 190 return self._succeed(self.handle.setParam(self.caller_id, key, value))
191
192 - def getParam(self, key):
193 """ 194 Retrieve parameter value from server. 195 @param key: parameter to lookup. If key is a namespace, 196 getParam() will return a parameter tree. 197 @type key: str 198 getParam() will return a parameter tree. 199 200 @return: parameterValue. If key is a namespace, 201 the return value will be a dictionary, where each key is a 202 parameter in that namespace. Sub-namespaces are also 203 represented as dictionaries. 204 @rtype: XMLRPCLegalValue 205 """ 206 return self._succeed(self.handle.getParam(self.caller_id, key))
207
208 - def searchParam(self, key):
209 """ 210 Search for parameter key on parameter server. Search starts in caller's namespace and proceeds 211 upwards through parent namespaces until Parameter Server finds a matching key. 212 213 searchParam's behavior is to search for the first partial match. 214 For example, imagine that there are two 'robot_description' parameters:: 215 216 /robot_description 217 /robot_description/arm 218 /robot_description/base 219 /pr2/robot_description 220 /pr2/robot_description/base 221 222 If I start in the namespace /pr2/foo and search for 223 'robot_description', searchParam will match 224 /pr2/robot_description. If I search for 'robot_description/arm' 225 it will return /pr2/robot_description/arm, even though that 226 parameter does not exist (yet). 227 228 @param key: parameter key to search for. 229 @type key: str 230 @return: foundKey 231 @rtype: str 232 """ 233 return self._succeed(self.handle.searchParam(self.caller_id, key))
234
235 - def subscribeParam(self, caller_api, key):
236 """ 237 Retrieve parameter value from server and subscribe to updates to that param. See 238 paramUpdate() in the Node API. 239 @param key: parameter to lookup. 240 @type key: str 241 @param caller_api: API URI for paramUpdate callbacks. 242 @type caller_api: str 243 @return: parameterValue. parameterValue is an empty dictionary if the parameter has not been set yet. 244 @rtype: XMLRPCLegalValue 245 """ 246 return self._succeed(self.handle.subscribeParam(self.caller_id, caller_api, key))
247
248 - def unsubscribeParam(self, caller_api, key):
249 """ 250 Retrieve parameter value from server and subscribe to updates to that param. See 251 paramUpdate() in the Node API. 252 @param key: parameter to lookup. 253 @type key: str 254 @param caller_api: API URI for paramUpdate callbacks. 255 @type caller_api: str 256 @return: numUnsubscribed. If numUnsubscribed is zero it means that the caller was not subscribed to the parameter. 257 @rtype: int 258 """ 259 return self._succeed(self.handle.unsubscribeParam(self.caller_id, caller_api, key))
260
261 - def hasParam(self, key):
262 """ 263 Check if parameter is stored on server. 264 @param key: parameter to check 265 @type key: str 266 @return: [code, statusMessage, hasParam] 267 @rtype: [int, str, bool] 268 """ 269 return self._succeed(self.handle.hasParam(self.caller_id, key))
270
271 - def getParamNames(self):
272 """ 273 Get list of all parameter names stored on this server. 274 This does not adjust parameter names for caller's scope. 275 276 @return: [code, statusMessage, parameterNameList] 277 @rtype: [int, str, [str]] 278 """ 279 return self._succeed(self.handle.getParamNames(self.caller_id))
280 281 282 ################################################################################ 283
284 - def getPid(self):
285 """ 286 Get the PID of this server 287 @return: serverProcessPID 288 @rtype: int 289 @raise rosgraph.masterapi.Error: if Master returns ERROR. 290 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 291 """ 292 return self._succeed(self.handle.getPid(self.caller_id))
293
294 - def getUri(self):
295 """ 296 Get the URI of this Master 297 @return: masterUri 298 @rtype: str 299 @raise rosgraph.masterapi.Error: if Master returns ERROR. 300 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 301 """ 302 return self._succeed(self.handle.getUri(self.caller_id))
303
304 - def registerService(self, service, service_api, caller_api):
305 """ 306 Register the caller as a provider of the specified service. 307 @param service str: Fully-qualified name of service 308 @param service_api str: Service URI 309 @param caller_api str: XML-RPC URI of caller node 310 @return: ignore 311 @rtype: int 312 @raise rosgraph.masterapi.Error: if Master returns ERROR. 313 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 314 """ 315 return self._succeed(self.handle.registerService(self.caller_id, service, service_api, caller_api))
316
317 - def lookupService(self, service):
318 """ 319 Lookup all provider of a particular service. 320 @param service: fully-qualified name of service to lookup. 321 @type: service: str 322 @return (int, str, str): (code, message, serviceUrl). service URL is provides 323 and address and port of the service. Fails if there is no provider. 324 @raise rosgraph.masterapi.Error: if Master returns ERROR. 325 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 326 """ 327 return self._succeed(self.handle.lookupService(self.caller_id, service))
328 329
330 - def unregisterService(self, service, service_api):
331 """ 332 Unregister the caller as a provider of the specified service. 333 @param service: Fully-qualified name of service 334 @type service: str 335 @param service_api: API URI of service to unregister. Unregistration will only occur if current 336 registration matches. 337 @type service_api: str 338 @return: (code, message, numUnregistered). Number of unregistrations (either 0 or 1). 339 If this is zero it means that the caller was not registered as a service provider. 340 The call still succeeds as the intended final state is reached. 341 @rtype: (int, str, int) 342 @raise rosgraph.masterapi.Error: if Master returns ERROR. 343 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 344 """ 345 return self._succeed(self.handle.unregisterService(self.caller_id, service, service_api))
346 347
348 - def registerSubscriber(self, topic, topic_type, caller_api):
349 """ 350 Subscribe the caller to the specified topic. In addition to receiving 351 a list of current publishers, the subscriber will also receive notifications 352 of new publishers via the publisherUpdate API. 353 @param topic str: Fully-qualified name of topic to subscribe to. 354 @param topic_type: Datatype for topic. Must be a package-resource name, i.e. the .msg name. 355 @type topic_type: str 356 @param caller_api: XML-RPC URI of caller node for new publisher notifications 357 @type caller_api: str 358 @return: (code, message, publishers). Publishers is a list of XMLRPC API URIs 359 for nodes currently publishing the specified topic. 360 @rtype: (int, str, list(str)) 361 @raise rosgraph.masterapi.Error: if Master returns ERROR. 362 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 363 """ 364 return self._succeed(self.handle.registerSubscriber(self.caller_id, topic, topic_type, caller_api))
365 366
367 - def unregisterSubscriber(self, topic, caller_api):
368 """ 369 Unregister the caller as a publisher of the topic. 370 @param topic: Fully-qualified name of topic to unregister. 371 @type topic: str 372 @param caller_api: API URI of service to unregister. Unregistration will only occur if current 373 @type caller_api: str 374 registration matches. 375 @return: (code, statusMessage, numUnsubscribed). 376 If numUnsubscribed is zero it means that the caller was not registered as a subscriber. 377 The call still succeeds as the intended final state is reached. 378 @rtype: (int, str, int) 379 @raise rosgraph.masterapi.Error: if Master returns ERROR. 380 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 381 """ 382 return self._succeed(self.handle.unregisterSubscriber(self.caller_id, topic, caller_api))
383
384 - def registerPublisher(self, topic, topic_type, caller_api):
385 """ 386 Register the caller as a publisher the topic. 387 @param topic: Fully-qualified name of topic to register. 388 @type topic: str 389 @param topic_type: Datatype for topic. Must be a 390 package-resource name, i.e. the .msg name. 391 @type topic_type: str 392 @param caller_api str: ROS caller XML-RPC API URI 393 @type caller_api: str 394 @return: subscriberApis. 395 List of current subscribers of topic in the form of XMLRPC URIs. 396 @rtype: [str] 397 @raise rosgraph.masterapi.Error: if Master returns ERROR. 398 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 399 """ 400 return self._succeed(self.handle.registerPublisher(self.caller_id, topic, topic_type, caller_api))
401
402 - def unregisterPublisher(self, topic, caller_api):
403 """ 404 Unregister the caller as a publisher of the topic. 405 @param topic: Fully-qualified name of topic to unregister. 406 @type topic: str 407 @param caller_api str: API URI of service to 408 unregister. Unregistration will only occur if current 409 registration matches. 410 @type caller_api: str 411 @return: numUnregistered. 412 If numUnregistered is zero it means that the caller was not registered as a publisher. 413 The call still succeeds as the intended final state is reached. 414 @rtype: int 415 @raise rosgraph.masterapi.Error: if Master returns ERROR. 416 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 417 """ 418 return self._succeed(self.handle.unregisterPublisher(self.caller_id, topic, caller_api))
419
420 - def lookupNode(self, node_name):
421 """ 422 Get the XML-RPC URI of the node with the associated 423 name/caller_id. This API is for looking information about 424 publishers and subscribers. Use lookupService instead to lookup 425 ROS-RPC URIs. 426 @param node: name of node to lookup 427 @type node: str 428 @return: URI 429 @rtype: str 430 @raise rosgraph.masterapi.Error: if Master returns ERROR. 431 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 432 """ 433 return self._succeed(self.handle.lookupNode(self.caller_id, node_name))
434
435 - def getPublishedTopics(self, subgraph):
436 """ 437 Get list of topics that can be subscribed to. This does not return topics that have no publishers. 438 See L{getSystemState()} to get more comprehensive list. 439 @param subgraph: Restrict topic names to match within the specified subgraph. Subgraph namespace 440 is resolved relative to the caller's namespace. Use '' to specify all names. 441 @type subgraph: str 442 @return: [[topic1, type1]...[topicN, typeN]] 443 @rtype: [[str, str],] 444 @raise rosgraph.masterapi.Error: if Master returns ERROR. 445 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 446 """ 447 return self._succeed(self.handle.getPublishedTopics(self.caller_id, subgraph))
448
449 - def getTopicTypes(self):
450 """ 451 Retrieve list topic names and their types. 452 453 New in ROS 1.2. 454 455 @rtype: (int, str, [[str,str]] ) 456 @return: (code, statusMessage, topicTypes). topicTypes is a list of [topicName, topicType] pairs. 457 """ 458 return self._succeed(self.handle.getTopicTypes(self.caller_id))
459
460 - def getSystemState(self):
461 """ 462 Retrieve list representation of system state (i.e. publishers, subscribers, and services). 463 @rtype: [[str,[str]], [str,[str]], [str,[str]]] 464 @return: systemState 465 466 System state is in list representation:: 467 [publishers, subscribers, services]. 468 469 publishers is of the form:: 470 [ [topic1, [topic1Publisher1...topic1PublisherN]] ... ] 471 472 subscribers is of the form:: 473 [ [topic1, [topic1Subscriber1...topic1SubscriberN]] ... ] 474 475 services is of the form:: 476 [ [service1, [service1Provider1...service1ProviderN]] ... ] 477 478 @raise rosgraph.masterapi.Error: if Master returns ERROR. 479 @raise rosgraph.masterapi.Failure: if Master returns FAILURE. 480 """ 481 return self._succeed(self.handle.getSystemState(self.caller_id)) 482