$search
00001 # Software License Agreement (BSD License) 00002 # 00003 # Copyright (c) 2010, Willow Garage, Inc. 00004 # All rights reserved. 00005 # 00006 # Redistribution and use in source and binary forms, with or without 00007 # modification, are permitted provided that the following conditions 00008 # are met: 00009 # 00010 # * Redistributions of source code must retain the above copyright 00011 # notice, this list of conditions and the following disclaimer. 00012 # * Redistributions in binary form must reproduce the above 00013 # copyright notice, this list of conditions and the following 00014 # disclaimer in the documentation and/or other materials provided 00015 # with the distribution. 00016 # * Neither the name of Willow Garage, Inc. nor the names of its 00017 # contributors may be used to endorse or promote products derived 00018 # from this software without specific prior written permission. 00019 # 00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00031 # POSSIBILITY OF SUCH DAMAGE. 00032 # 00033 # Revision $Id: handler.py 11413 2010-10-05 23:28:20Z kwc $ 00034 00035 import os 00036 import sys 00037 import socket 00038 import threading 00039 import traceback 00040 import time 00041 import urlparse 00042 00043 from roslib.xmlrpc import XmlRpcHandler 00044 00045 import rospy.names 00046 00047 import rospy.impl.tcpros 00048 00049 from rospy.core import global_name, is_topic 00050 from rospy.impl.registration import get_topic_manager 00051 from rospy.impl.validators import non_empty, ParameterInvalid 00052 00053 from rospy.impl.masterslave import apivalidate 00054 00055 # pseudo-validators ############################### 00056 # these validators actually return tuples instead of a function and it is up to a custom 00057 # validator on the class itself to perform the validation 00058 def is_publishers_list(paramName): 00059 return ('is_publishers_list', paramName) 00060 00061 class ProxyHandler(XmlRpcHandler): 00062 00063 def __init__(self, name, master_uri, topic_manager, protocol_handlers): 00064 """ 00065 Variant handler for proxy 00066 @param name: ROS name of this node 00067 @type name: str 00068 @param master_uri: URI of master node, or None if this node is the master 00069 @type master_uri: str 00070 """ 00071 super(ProxyHandler, self).__init__() 00072 self.master_uri = master_uri 00073 self.name = name 00074 self.uri = None 00075 self.done = False 00076 00077 self.protocol_handlers = protocol_handlers 00078 00079 self.reg_man = None 00080 00081 # this is the key difference from the normal handler 00082 self.topic_man = topic_manager 00083 00084 ############################################################################### 00085 # INTERNAL 00086 00087 def _ready(self, uri): 00088 """ 00089 @param uri: XML-RPC URI 00090 @type uri: str 00091 callback from ROSNode to inform handler of correct i/o information 00092 """ 00093 self.uri = uri 00094 00095 def _custom_validate(self, validation, param_name, param_value, caller_id): 00096 """ 00097 Implements validation rules that require access to internal ROSHandler state. 00098 @param validation: name of validation rule to use 00099 @type validation: str 00100 @param param_name: name of parameter being validated 00101 @type param_name: str 00102 @param param_value str: value of parameter 00103 @type param_value: str 00104 @param caller_id: value of caller_id parameter to API method 00105 @type caller_id: str 00106 @raise ParameterInvalid: if the parameter does not meet validation 00107 @return: new value for parameter, after validation 00108 """ 00109 if validation == 'is_publishers_list': 00110 if not type(param_value) == list: 00111 raise ParameterInvalid("ERROR: param [%s] must be a list"%param_name) 00112 for v in param_value: 00113 if not isinstance(v, basestring): 00114 raise ParameterInvalid("ERROR: param [%s] must be a list of strings"%param_name) 00115 parsed = urlparse.urlparse(v) 00116 if not parsed[0] or not parsed[1]: #protocol and host 00117 raise ParameterInvalid("ERROR: param [%s] does not contain valid URLs [%s]"%(param_name, v)) 00118 return param_value 00119 else: 00120 raise ParameterInvalid("ERROR: param [%s] has an unknown validation type [%s]"%(param_name, validation)) 00121 00122 ############################################################################### 00123 # EXTERNAL API 00124 00125 @apivalidate([]) 00126 def getBusStats(self, caller_id): 00127 # not supported 00128 return 1, '', [[], [], []] 00129 00130 @apivalidate([]) 00131 def getBusInfo(self, caller_id): 00132 # not supported 00133 return 1, '', [[], [], []] 00134 00135 @apivalidate('') 00136 def getMasterUri(self, caller_id): 00137 return 1, self.master_uri, self.master_uri 00138 00139 @apivalidate(0, (None, )) 00140 def shutdown(self, caller_id, msg=''): 00141 return -1, "not authorized", 0 00142 00143 @apivalidate(-1) 00144 def getPid(self, caller_id): 00145 return -1, "not authorized", 0 00146 00147 ############################################################################### 00148 # PUB/SUB APIS 00149 00150 @apivalidate([]) 00151 def getSubscriptions(self, caller_id): 00152 """Retrieve a list of topics that this node subscribes to 00153 @param caller_id: ROS caller id 00154 @type caller_id: str 00155 @return [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]]: list of topics this node subscribes to 00156 """ 00157 return 1, "subscriptions", self.topic_man.get_subscriptions() 00158 00159 @apivalidate([]) 00160 def getPublications(self, caller_id): 00161 """ 00162 Retrieve a list of topics that this node publishes. 00163 @param caller_id: ROS caller id 00164 @type caller_id: str 00165 @return: list of topics published by this node 00166 @rtype: [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]] 00167 """ 00168 return 1, "publications", self.topic_man.get_publications() 00169 00170 @apivalidate(-1, (global_name('parameter_key'), None)) 00171 def paramUpdate(self, caller_id, parameter_key, parameter_value): 00172 """ 00173 Callback from master of current publisher list for specified topic. 00174 @param caller_id: ROS caller id 00175 @type caller_id: str 00176 @param parameter_key str: parameter name, globally resolved 00177 @type parameter_key: str 00178 @param parameter_value New parameter value 00179 @type parameter_value: XMLRPC-legal value 00180 @return: [code, status, ignore]. If code is -1 ERROR, the node 00181 is not subscribed to parameter_key 00182 @rtype: [int, str, int] 00183 """ 00184 # not supported 00185 return -1, 'not authorized', 0 00186 00187 @apivalidate(-1, (is_topic('topic'), is_publishers_list('publishers'))) 00188 def publisherUpdate(self, caller_id, topic, publishers): 00189 """ 00190 Callback from master of current publisher list for specified topic. 00191 @param caller_id: ROS caller id 00192 @type caller_id: str 00193 @param topic str: topic name 00194 @type topic: str 00195 @param publishers: list of current publishers for topic in the form of XMLRPC URIs 00196 @type publishers: [str] 00197 @return: [code, status, ignore] 00198 @rtype: [int, str, int] 00199 """ 00200 if self.reg_man: 00201 for uri in publishers: 00202 self.reg_man.publisher_update(topic, publishers) 00203 00204 @apivalidate([], (is_topic('topic'), non_empty('protocols'))) 00205 def requestTopic(self, caller_id, topic, protocols): 00206 """ 00207 Publisher node API method called by a subscriber node. 00208 00209 Request that source allocate a channel for communication. Subscriber provides 00210 a list of desired protocols for communication. Publisher returns the 00211 selected protocol along with any additional params required for 00212 establishing connection. For example, for a TCP/IP-based connection, 00213 the source node may return a port number of TCP/IP server. 00214 @param caller_id str: ROS caller id 00215 @type caller_id: str 00216 @param topic: topic name 00217 @type topic: str 00218 @param protocols: list of desired 00219 protocols for communication in order of preference. Each 00220 protocol is a list of the form [ProtocolName, 00221 ProtocolParam1, ProtocolParam2...N] 00222 @type protocols: [[str, XmlRpcLegalValue*]] 00223 @return: [code, msg, protocolParams]. protocolParams may be an 00224 empty list if there are no compatible protocols. 00225 @rtype: [int, str, [str, XmlRpcLegalValue*]] 00226 """ 00227 if not self.topic_man.has_publication(topic): 00228 return -1, "Not a publisher of [%s]"%topic, [] 00229 for protocol in protocols: #simple for now: select first implementation 00230 protocol_id = protocol[0] 00231 for h in self.protocol_handlers: 00232 if h.supports(protocol_id): 00233 return h.init_publisher(topic, protocol) 00234 return 0, "no supported protocol implementations", [] 00235 00236