handler.py
Go to the documentation of this file.
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 rosgraph.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     


rosproxy
Author(s): Ken Conley
autogenerated on Mon Dec 2 2013 11:36:45