PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/clients/rospy/src/rospy/impl/udpros.py

https://gitlab.com/F34140r/ros_comm
Python | 306 lines | 199 code | 21 blank | 86 comment | 12 complexity | aa12a4cd2455684dcf431f91e9ae6d3d MD5 | raw file
Possible License(s): LGPL-2.1
  1. # Software License Agreement (BSD License)
  2. #
  3. # Copyright (c) 2008, 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$
  34. from __future__ import print_function
  35. """
  36. UDPROS connection protocol.
  37. """
  38. ## UDPROS connection protocol.
  39. # http://ros.org/wiki/ROS/UDPROS
  40. #
  41. import rosgraph.network
  42. import rospy.impl.registration
  43. import rospy.impl.transport
  44. def get_max_datagram_size():
  45. #TODO
  46. return 1024
  47. class UDPROSHandler(rospy.transport.ProtocolHandler):
  48. """
  49. rospy protocol handler for UDPROS. Stores the datagram server if necessary.
  50. """
  51. def __init__(self, port=0):
  52. """
  53. ctor
  54. """
  55. self.port = port
  56. self.buff_size = get_max_datagram_size()
  57. def init_server(self):
  58. """
  59. Initialize and start the server thread, if not already initialized.
  60. """
  61. if self.server is not None:
  62. return
  63. if rosgraph.network.use_ipv6():
  64. s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
  65. else:
  66. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  67. s.bind((rosgraph.network.get_bind_address(), self.port))
  68. if self.port == 0:
  69. self.port = s.getsockname()[1]
  70. self.server = s
  71. threading.start_new_thread(self.run, ())
  72. def run(self):
  73. buff_size = self.buff_size
  74. try:
  75. while not rospy.core.is_shutdown():
  76. data = self.server.recvfrom(self.buff_size)
  77. print("received packet")
  78. #TODO
  79. except:
  80. #TODO: log
  81. pass
  82. def shutdown(self):
  83. if self.sock is not None:
  84. self.sock.close()
  85. def create_transport(self, topic_name, pub_uri, protocol_params):
  86. """
  87. Connect to topic resolved_name on Publisher pub_uri using UDPROS.
  88. @param resolved_name str: resolved topic name
  89. @type resolved_name: str
  90. @param pub_uri: XML-RPC URI of publisher
  91. @type pub_uri: str
  92. @param protocol_params: protocol parameters to use for connecting
  93. @type protocol_params: [XmlRpcLegal]
  94. @return: code, message, debug
  95. @rtype: (int, str, int)
  96. """
  97. #Validate protocol params = [UDPROS, address, port, headers]
  98. if type(protocol_params) != list or len(protocol_params) != 4:
  99. return 0, "ERROR: invalid UDPROS parameters", 0
  100. if protocol_params[0] != UDPROS:
  101. return 0, "INTERNAL ERROR: protocol id is not UDPROS: %s"%id, 0
  102. #TODO: get connection_id and buffer size from params
  103. id, dest_addr, dest_port, headers = protocol_params
  104. self.init_server()
  105. #TODO: parse/validate headers
  106. sub = rospy.registration.get_topic_manager().get_subscriber_impl(topic_name)
  107. # Create Transport
  108. # TODO: create just a single 'connection' instance to represent
  109. # all UDP connections. 'connection' can take care of unifying
  110. # publication if addresses are the same
  111. transport = UDPTransport(protocol, topic_name, sub.receive_callback)
  112. # Attach connection to _SubscriberImpl
  113. if sub.add_connection(transport): #pass udp connection to handler
  114. return 1, "Connected topic[%s]. Transport impl[%s]"%(topic_name, transport.__class__.__name__), dest_port
  115. else:
  116. transport.close()
  117. return 0, "ERROR: Race condition failure: duplicate topic subscriber [%s] was created"%(topic_name), 0
  118. def supports(self, protocol):
  119. """
  120. @param protocol: name of protocol
  121. @type protocol: str
  122. @return: True if protocol is supported
  123. @rtype: bool
  124. """
  125. return protocol == UDPROS
  126. def get_supported(self):
  127. """
  128. Get supported protocols
  129. """
  130. return [[UDPROS]]
  131. def init_publisher(self, topic_name, protocol_params):
  132. """
  133. Initialize this node to start publishing to a new UDP location.
  134. @param resolved_name: topic name
  135. @type resolved__name: str
  136. @param protocol_params: requested protocol
  137. parameters. protocol[0] must be the string 'UDPROS'
  138. @type protocol_params: [str, value*]
  139. @return: (code, msg, [UDPROS, addr, port])
  140. @rtype: (int, str, list)
  141. """
  142. if protocol_params[0] != UDPROS:
  143. return 0, "Internal error: protocol does not match UDPROS: %s"%protocol, []
  144. #TODO
  145. _, header, host, port, max_datagram_size = protocol_params
  146. #TODO: connection_id, max_datagraph_size
  147. return 1, "ready", [UDPROS]
  148. def topic_connection_handler(self, sock, client_addr, header):
  149. """
  150. Process incoming topic connection. Reads in topic name from
  151. handshake and creates the appropriate L{TCPROSPub} handler for the
  152. connection.
  153. @param sock: socket connection
  154. @type sock: socket.socket
  155. @param client_addr: client address
  156. @type client_addr: (str, int)
  157. @param header: key/value pairs from handshake header
  158. @type header: dict
  159. @return: error string or None
  160. @rtype: str
  161. """
  162. for required in ['topic', 'md5sum', 'callerid']:
  163. if not required in header:
  164. return "Missing required '%s' field"%required
  165. else:
  166. resolved_topic_name = header['topic']
  167. md5sum = header['md5sum']
  168. tm = rospy.registration.get_topic_manager()
  169. topic = tm.get_publisher_impl(resolved_topic_name)
  170. if not topic:
  171. return "[%s] is not a publisher of [%s]. Topics are %s"%(rospy.names.get_caller_id(), resolved_topic_name, tm.get_publications())
  172. elif md5sum != rospy.names.TOPIC_ANYTYPE and md5sum != topic.data_class._md5sum:
  173. actual_type = topic.data_class._type
  174. # check to see if subscriber sent 'type' header. If they did, check that
  175. # types are same first as this provides a better debugging message
  176. if 'type' in header:
  177. requested_type = header['type']
  178. if requested_type != actual_type:
  179. return "topic types do not match: [%s] vs. [%s]"%(requested_type, actual_type)
  180. else:
  181. # defaults to actual type
  182. requested_type = actual_type
  183. return "Client [%s] wants topic [%s] to have datatype/md5sum [%s/%s], but our version has [%s/%s] Dropping connection."%(header['callerid'], resolved_topic_name, requested_type, md5sum, actual_type, topic.data_class._md5sum)
  184. else:
  185. #TODO:POLLING if polling header is present, have to spin up receive loop as well
  186. # #1334: tcp_nodelay support from subscriber option
  187. if 'tcp_nodelay' in header:
  188. tcp_nodelay = True if header['tcp_nodelay'].strip() == '1' else False
  189. else:
  190. tcp_nodelay = self.tcp_nodelay_map.get(resolved_topic_name, False)
  191. _configure_pub_socket(sock, tcp_nodelay)
  192. protocol = TCPROSPub(resolved_topic_name, topic.data_class, is_latch=topic.is_latch, headers=topic.headers)
  193. transport = TCPROSTransport(protocol, resolved_topic_name)
  194. transport.set_socket(sock, header['callerid'])
  195. transport.write_header()
  196. topic.add_connection(transport)
  197. ## UDPROS communication routines
  198. class UDPROSTransport(rospy.transport.Transport):
  199. transport_type = 'UDPROS'
  200. def __init__(self, protocol, name, header):
  201. """
  202. ctor
  203. @param name: topic name
  204. @type name: str:
  205. @param protocol: protocol implementation
  206. @param protocol: UDPROSTransportProtocol
  207. @param header: handshake header if transport handshake header was
  208. already read off of transport.
  209. @type header: dict
  210. @throws TransportInitError: if transport cannot be initialized according to arguments
  211. """
  212. super(UDPROSTransport, self).__init__(protocol.direction, name=name)
  213. if not name:
  214. raise TransportInitError("Unable to initialize transport: name is not set")
  215. self.done = False
  216. self.header = header
  217. def send_message(self, msg, seq):
  218. """
  219. Convenience routine for services to send a message across a
  220. particular connection. NOTE: write_data is much more efficient
  221. if same message is being sent to multiple connections. Not
  222. threadsafe.
  223. @param msg: message to send
  224. @type msg: Msg
  225. @param seq: sequence number for message
  226. @type seq: int
  227. @raise TransportException: if error occurred sending message
  228. """
  229. # this will call write_data(), so no need to keep track of stats
  230. serialize_message(self.write_buff, seq, msg)
  231. self.write_data(self.write_buff.getvalue())
  232. self.write_buff.truncate(0)
  233. def write_data(self, data):
  234. """
  235. Write raw data to transport
  236. @raise TransportInitialiationError: could not be initialized
  237. @raise TransportTerminated: no longer open for publishing
  238. """
  239. # TODO
  240. # - cut into packets
  241. # write to address
  242. pass
  243. def receive_once(self):
  244. """
  245. block until messages are read off of socket
  246. @return: list of newly received messages
  247. @rtype: [Msg]
  248. @raise TransportException: if unable to receive message due to error
  249. """
  250. pass
  251. ## Receive messages until shutdown
  252. ## @param self
  253. ## @param msgs_callback fn([msg]): callback to invoke for new messages received
  254. def receive_loop(self, msgs_callback):
  255. pass
  256. ## close i/o and release resources
  257. def close(super):
  258. self(UDPROSTransport, self).close()
  259. #TODO
  260. self.done = True
  261. _handler = UDPROSHandler()
  262. def get_handler():
  263. return _handler