def unregister(self): """ unpublish/unsubscribe from topic. Topic instance is no longer valid after this call. Additional calls to unregister() have no effect. """ # as we don't guard unregister, have to protect value of # resolved_name for release_impl call resolved_name = self.resolved_name if resolved_name and self.impl: get_topic_manager().release_impl(self.reg_type, resolved_name) self.impl = self.resolved_name = self.type = self.md5sum = self.data_class = None
def getBusInfo(self, caller_id): """ Retrieve transport/topic connection information @param caller_id: ROS caller id @type caller_id: str """ return 1, "bus info", get_topic_manager().get_pub_sub_info()
def requestTopic(self, caller_id, topic, protocols): """ Publisher node API method called by a subscriber node. Request that source allocate a channel for communication. Subscriber provides a list of desired protocols for communication. Publisher returns the selected protocol along with any additional params required for establishing connection. For example, for a TCP/IP-based connection, the source node may return a port number of TCP/IP server. @param caller_id str: ROS caller id @type caller_id: str @param topic: topic name @type topic: str @param protocols: list of desired protocols for communication in order of preference. Each protocol is a list of the form [ProtocolName, ProtocolParam1, ProtocolParam2...N] @type protocols: [[str, XmlRpcLegalValue*]] @return: [code, msg, protocolParams]. protocolParams may be an empty list if there are no compatible protocols. @rtype: [int, str, [str, XmlRpcLegalValue*]] """ if not get_topic_manager().has_publication(topic): return -1, "Not a publisher of [%s]" % topic, [] for protocol in protocols: #simple for now: select first implementation protocol_id = protocol[0] for h in self.protocol_handlers: if h.supports(protocol_id): _logger.debug("requestTopic[%s]: choosing protocol %s", topic, protocol_id) return h.init_publisher(topic, protocol) return 0, "no supported protocol implementations", []
def requestTopic(self, caller_id, topic, protocols): """ Publisher node API method called by a subscriber node. Request that source allocate a channel for communication. Subscriber provides a list of desired protocols for communication. Publisher returns the selected protocol along with any additional params required for establishing connection. For example, for a TCP/IP-based connection, the source node may return a port number of TCP/IP server. @param caller_id str: ROS caller id @type caller_id: str @param topic: topic name @type topic: str @param protocols: list of desired protocols for communication in order of preference. Each protocol is a list of the form [ProtocolName, ProtocolParam1, ProtocolParam2...N] @type protocols: [[str, XmlRpcLegalValue*]] @return: [code, msg, protocolParams]. protocolParams may be an empty list if there are no compatible protocols. @rtype: [int, str, [str, XmlRpcLegalValue*]] """ if not get_topic_manager().has_publication(topic): return -1, "Not a publisher of [%s]"%topic, [] for protocol in protocols: #simple for now: select first implementation protocol_id = protocol[0] for h in self.protocol_handlers: if h.supports(protocol_id): _logger.debug("requestTopic[%s]: choosing protocol %s", topic, protocol_id) return h.init_publisher(topic, protocol) return 0, "no supported protocol implementations", []
def _rosout(level, msg, fname, line, func): global _in_rosout try: if _rosout_pub is not None: # protect against infinite recursion if not _in_rosout: try: _in_rosout = True msg = str(msg) # check parameter server/cache for omit_topics flag # the same parameter is checked in rosout_appender.cpp for the same purpose # parameter accesses are cached automatically in python if rospy.has_param("/rosout_disable_topics_generation"): disable_topics_ = rospy.get_param("/rosout_disable_topics_generation") else: disable_topics_ = False if not disable_topics_: topics = get_topic_manager().get_topics() else: topics = "" l = Log(level=level, name=str(rospy.names.get_caller_id()), msg=str(msg), topics=topics, file=fname, line=line, function=func) l.header.stamp = Time.now() _rosout_pub.publish(l) finally: _in_rosout = False except Exception as e: #traceback.print_exc() # don't use logerr in this case as that is recursive here logger = logging.getLogger("rospy.rosout") logger.error("Unable to report rosout: %s\n%s", e, traceback.format_exc()) return False
def getSubscriptions(self, caller_id): """Retrieve a list of topics that this node subscribes to @param caller_id: ROS caller id @type caller_id: str @return [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]]: list of topics this node subscribes to """ return 1, "subscriptions", get_topic_manager().get_subscriptions()
def _unregisterPublisherCallback(self, data): name = data[NODE_NAME] topic = data[TOPIC_NAME] uri = data[XMLRPC_URI] uri_list = [uri] tm = get_topic_manager() try: tm.lock.acquire() if tm.has_subscription(topic): self._logger.debug("I has sub topic, recv unregisSub: %s" % topic) get_node_handler().reg_man.reg_removed(topic, uri_list, Registration.SUB) except Exception as e: self._logger.error( "unregisterPublisherCallback error is %s" % e) self._logger.error(traceback.format_exc()) finally: tm.lock.release() try: self._pubs[topic].remove(uri) except KeyError: pass try: self._pub_history[topic].remove(self._encode(data)) except KeyError: pass
def _registerSubscriberCallback(self, data): name = data[NODE_NAME] topic = data[TOPIC_NAME] datatype = data[TOPIC_TYPE] uri = data[XMLRPC_URI] url_data = [uri] tmp = [topic, [name]] if tmp not in self._stat_info[1]: self._stat_info[1].append(tmp) tm = get_topic_manager() try: tm.lock.acquire() if tm.has_publication(topic): self._logger.debug("I has pub topic :%s" % topic) self.registerPublisher( get_node_handler().name, topic, datatype, get_node_handler().uri) except Exception as e: self._logger.error( "_registerSubscriberCallback error is %s" % e) self._logger.error(traceback.format_exc()) finally: tm.lock.release()
def _rosout(level, msg): global _in_rosout try: if _rosout_pub is not None: # protect against infinite recursion if not _in_rosout: try: _in_rosout = True msg = str(msg) topics = get_topic_manager().get_topics() l = Log(level=level, name=str(rospy.names.get_caller_id()), msg=str(msg), topics=topics) l.header.stamp = Time.now() _rosout_pub.publish(l) finally: _in_rosout = False except Exception as e: #traceback.print_exc() # don't use logerr in this case as that is recursive here logger = logging.getLogger("rospy.rosout") logger.error("Unable to report rosout: %s\n%s", e, traceback.format_exc()) return False
def _registerPublisherCallback(self, data): name = data[NODE_NAME] topic = data[TOPIC_NAME] topic_type = data[TOPIC_TYPE] topic_uri = data[XMLRPC_URI] url_data = [topic_uri] tmp = [topic, [name]] if tmp not in self._stat_info[0]: self._stat_info[0].append(tmp) topic_info = [x for x in self._topic_info if x[0] == topic] if topic_info: topic_info[0][1] = topic_type else: self._topic_info.append([topic, topic_type]) tm = get_topic_manager() try: tm.lock.acquire() if tm.has_subscription(topic): self._logger.debug("I has sub topic : %s" % topic) get_node_handler().publisherUpdate(name, topic, url_data) except Exception as e: self._logger.error( "registerPublisherCallback error is %s" % e) self._logger.error(traceback.format_exc()) finally: tm.lock.release()
def reregister(self): """ Resets some of the attributes in order to restore the state of the object before the `unregister` method was called Reversing the `Topic.unregister()` and `Subscriber.unregister()` methods from `here <http://docs.ros.org/api/rospy/html/rospy.topics-pysrc.html>`_. """ # Restore what was removed by the Topic.unregister() method self.impl = get_topic_manager().acquire_impl( self.reg_type, self._attributres_keeper['resolved_name'], self._attributres_keeper['data_class']) self.resolved_name = self._attributres_keeper['resolved_name'] self.data_class = self._attributres_keeper['data_class'] self.type = self.data_class._type self.md5sum = self.data_class._md5sum # Restore what was removed by the Subscriber.unregister() method if self._attributres_keeper['queue_size'] is not None: self.impl.set_queue_size(self._attributres_keeper['queue_size']) if self._attributres_keeper['buff_size'] != DEFAULT_BUFF_SIZE: self.impl.set_buff_size(self._attributres_keeper['buff_size']) if self._attributres_keeper['callback'] is not None: self.impl.add_callback(self._attributres_keeper['callback'], self._attributres_keeper['callback_args']) self.callback = self._attributres_keeper['callback'] self.callback_args = self._attributres_keeper['callback_args'] else: self.callback = self.callback_args = None if self._attributres_keeper['tcp_nodelay']: self.impl.set_tcp_nodelay(self._attributres_keeper['tcp_nodelay'])
def test_get_set_topic_manager(self): from rospy.impl.registration import get_topic_manager, set_topic_manager # rospy initialization sets this, but it is out of scope of # rospy.impl.registrations to test its value orig = get_topic_manager() try: self.assert_(orig is not None) class TopicManager(object): pass x = TopicManager() # currently untyped set_topic_manager(x) self.assertEquals(x, get_topic_manager()) set_topic_manager(None) self.assert_(get_topic_manager() is None) finally: set_topic_manager(orig)
def _connect_topic(self, topic, pub_uri): """ Connect subscriber to topic @param topic: topic name to connect @type topic: str @param pub_uri: API URI of topic publisher @type pub_uri: str @return: [code, msg, numConnects]. numConnects is the number of subscribers connected to the topic @rtype: [int, str, int] """ caller_id = rospy.names.get_caller_id() sub = get_topic_manager().get_subscriber_impl(topic) if not sub: return -1, "No subscriber for topic [%s]" % topic, 0 elif sub.has_connection(pub_uri): return 1, "_connect_topic[%s]: subscriber already connected to publisher [%s]" % ( topic, pub_uri), 0 #Negotiate with source for connection # - collect supported protocols protocols = [] for h in self.protocol_handlers: #currently only TCPROS protocols.extend(h.get_supported()) if not protocols: return 0, "ERROR: no available protocol handlers", 0 _logger.debug("connect[%s]: calling requestTopic(%s, %s, %s)", topic, caller_id, topic, str(protocols)) # 1) have to preserve original (unresolved) params as this may # go outside our graph # 2) xmlrpclib doesn't give us any way of affecting the # timeout other than affecting the global timeout. We need # to set a timeout to prevent infinite hangs. 60 seconds is # a *very* long time. All of the rospy code right now sets # individual socket timeouts, but this could potentially # affect user code. socket.setdefaulttimeout(60.) try: code, msg, result = \ xmlrpcapi(pub_uri).requestTopic(caller_id, topic, protocols) except Exception as e: return 0, "unable to requestTopic: %s" % str(e), 0 #Create the connection (if possible) if code <= 0: _logger.debug("connect[%s]: requestTopic did not succeed %s, %s", pub_uri, code, msg) return code, msg, 0 elif not result or type(protocols) != list: return 0, "ERROR: publisher returned invalid protocol choice: %s" % ( str(result)), 0 _logger.debug("connect[%s]: requestTopic returned protocol list %s", topic, result) protocol = result[0] for h in self.protocol_handlers: if h.supports(protocol): return h.create_transport(topic, pub_uri, result) return 0, "ERROR: publisher returned unsupported protocol choice: %s" % result, 0
def getPublications(self, caller_id): """ Retrieve a list of topics that this node publishes. @param caller_id: ROS caller id @type caller_id: str @return: list of topics published by this node. @rtype: [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]] """ return 1, "publications", get_topic_manager().get_publications()
def _connect_topic(self, topic, pub_uri): """ Connect subscriber to topic @param topic: topic name to connect @type topic: str @param pub_uri: API URI of topic publisher @type pub_uri: str @return: [code, msg, numConnects]. numConnects is the number of subscribers connected to the topic @rtype: [int, str, int] """ caller_id = rospy.names.get_caller_id() sub = get_topic_manager().get_subscriber_impl(topic) if not sub: return -1, "No subscriber for topic [%s]"%topic, 0 elif sub.has_connection(pub_uri): return 1, "_connect_topic[%s]: subscriber already connected to publisher [%s]"%(topic, pub_uri), 0 #Negotiate with source for connection # - collect supported protocols protocols = [] for h in self.protocol_handlers: #currently only TCPROS protocols.extend(h.get_supported()) if not protocols: return 0, "ERROR: no available protocol handlers", 0 _logger.debug("connect[%s]: calling requestTopic(%s, %s, %s)", topic, caller_id, topic, str(protocols)) # 1) have to preserve original (unresolved) params as this may # go outside our graph # 2) xmlrpclib doesn't give us any way of affecting the # timeout other than affecting the global timeout. We need # to set a timeout to prevent infinite hangs. 60 seconds is # a *very* long time. All of the rospy code right now sets # individual socket timeouts, but this could potentially # affect user code. socket.setdefaulttimeout(60.) try: code, msg, result = \ xmlrpcapi(pub_uri).requestTopic(caller_id, topic, protocols) except Exception as e: return 0, "unable to requestTopic: %s"%str(e), 0 #Create the connection (if possible) if code <= 0: _logger.debug("connect[%s]: requestTopic did not succeed %s, %s", pub_uri, code, msg) return code, msg, 0 elif not result or type(protocols) != list: return 0, "ERROR: publisher returned invalid protocol choice: %s"%(str(result)), 0 _logger.debug("connect[%s]: requestTopic returned protocol list %s", topic, result) protocol = result[0] for h in self.protocol_handlers: if h.supports(protocol): return h.create_transport(topic, pub_uri, result) return 0, "ERROR: publisher returned unsupported protocol choice: %s"%result, 0
def __init__(self, name, data_class, reg_type): """ @param name: graph resource name of topic, e.g. 'laser'. @type name: str @param data_class: message class for serialization @type data_class: L{Message} @param reg_type Registration.PUB or Registration.SUB @type reg_type: str @raise ValueError: if parameters are invalid """ if not name or not isstring(name): raise ValueError("topic name is not a non-empty string") try: if python3 == 1: name.encode("utf-8") else: name = name.encode("utf-8") except UnicodeError: raise ValueError("topic name must be ascii/utf-8 compatible") if data_class is None: raise ValueError("topic parameter 'data_class' is not initialized") if not type(data_class) == type: raise ValueError("data_class [%s] is not a class" % data_class) if not issubclass(data_class, genpy.Message): raise ValueError("data_class [%s] is not a message data class" % data_class.__class__.__name__) # #2202 if not rosgraph.names.is_legal_name(name): import warnings warnings.warn( "'%s' is not a legal ROS graph resource name. This may cause problems with other ROS tools" % name, stacklevel=2) # this is a bit ugly, but necessary due to the fact that we allow # topics and services to be initialized before the node if not rospy.core.is_initialized(): self.resolved_name = rospy.names.resolve_name_without_node_name( name) else: # init_node() has been called, so we can do normal resolution self.resolved_name = resolve_name(name) self.name = self.resolved_name # #1810 for backwards compatibility self.data_class = data_class self.type = data_class._type self.md5sum = data_class._md5sum self.reg_type = reg_type self.impl = get_topic_manager().acquire_impl(reg_type, self.resolved_name, data_class)
def getBusInfo(self, caller_id, client_ip_address): """ Retrieve transport/topic connection information @param caller_id: ROS caller id @type caller_id: str """ """ Only master is authorized to call this method """ if is_uri_match(self.masterUri, client_ip_address): auth_logger.warn("getBusInfo( %s, %s ) method not authorized" % (caller_id, client_ip_address)) return 1, "bus info", get_topic_manager().get_pub_sub_info() else: return -1, "method not authorized", []
def getPublications(self, caller_id, client_ip_address): """ Retrieve a list of topics that this node publishes. @param caller_id: ROS caller id @type caller_id: str @return: list of topics published by this node. @rtype: [int, str, [ [topic1, topicType1]...[topicN, topicTypeN]]] """ if not is_uri_match(self.masterUri, client_ip_address): auth_logger.warn( "getPublications( %s, %s ) method not authorized" % (caller_id, client_ip_address)) return -1, "method not authorized", [] return 1, "publications", get_topic_manager().get_publications()
def __init__(self, name, data_class, reg_type): """ @param name: graph resource name of topic, e.g. 'laser'. @type name: str @param data_class: message class for serialization @type data_class: L{Message} @param reg_type Registration.PUB or Registration.SUB @type reg_type: str @raise ValueError: if parameters are invalid """ if not name or not isstring(name): raise ValueError("topic name is not a non-empty string") try: if python3 == 1: name.encode("utf-8") else: name = name.encode("utf-8") except UnicodeError: raise ValueError("topic name must be ascii/utf-8 compatible") if data_class is None: raise ValueError("topic parameter 'data_class' is not initialized") if not type(data_class) == type: raise ValueError("data_class [%s] is not a class" % data_class) if not issubclass(data_class, genpy.Message): raise ValueError("data_class [%s] is not a message data class" % data_class.__class__.__name__) # #2202 if not rosgraph.names.is_legal_name(name): import warnings warnings.warn( "'%s' is not a legal ROS graph resource name. This may cause problems with other ROS tools" % name, stacklevel=2, ) # this is a bit ugly, but necessary due to the fact that we allow # topics and services to be initialized before the node if not rospy.core.is_initialized(): self.resolved_name = rospy.names.resolve_name_without_node_name(name) else: # init_node() has been called, so we can do normal resolution self.resolved_name = resolve_name(name) self.name = self.resolved_name # #1810 for backwards compatibility self.data_class = data_class self.type = data_class._type self.md5sum = data_class._md5sum self.reg_type = reg_type self.impl = get_topic_manager().acquire_impl(reg_type, self.resolved_name, data_class)
def getBusStats(self, caller_id): """ Retrieve transport/topic statistics @param caller_id: ROS caller id @type caller_id: str @return: [publishStats, subscribeStats, serviceStats]:: publishStats: [[topicName, messageDataSent, pubConnectionData]...[topicNameN, messageDataSentN, pubConnectionDataN]] pubConnectionData: [connectionId, bytesSent, numSent, connected]* . subscribeStats: [[topicName, subConnectionData]...[topicNameN, subConnectionDataN]] subConnectionData: [connectionId, bytesReceived, dropEstimate, connected]* . dropEstimate is -1 if no estimate. serviceStats: not sure yet, probably akin to [numRequests, bytesReceived, bytesSent] """ pub_stats, sub_stats = get_topic_manager().get_pub_sub_stats() #TODO: serviceStats return 1, '', [pub_stats, sub_stats, []]
def getBusStats(self, caller_id, client_ip_address): """ Retrieve transport/topic statistics @param caller_id: ROS caller id @type caller_id: str @return: [publishStats, subscribeStats, serviceStats]:: publishStats: [[topicName, messageDataSent, pubConnectionData]...[topicNameN, messageDataSentN, pubConnectionDataN]] pubConnectionData: [connectionId, bytesSent, numSent, connected]* . subscribeStats: [[topicName, subConnectionData]...[topicNameN, subConnectionDataN]] subConnectionData: [connectionId, bytesReceived, dropEstimate, connected]* . dropEstimate is -1 if no estimate. serviceStats: not sure yet, probably akin to [numRequests, bytesReceived, bytesSent] """ is_master = is_uri_match(self.masterUri, client_ip_address) pub_stats, sub_stats = get_topic_manager().get_pub_sub_stats() #TODO: serviceStats if is_master: return 1, '', [pub_stats, sub_stats, []] else: return -1, "Only master authorized to call getBusStats()", []
def _connect_topic(self, topic, pub_uri): """ Connect subscriber to topic. @param topic: Topic name to connect. @type topic: str @param pub_uri: API URI of topic publisher. @type pub_uri: str @return: [code, msg, numConnects]. numConnects is the number of subscribers connected to the topic. @rtype: [int, str, int] """ caller_id = rospy.names.get_caller_id() sub = get_topic_manager().get_subscriber_impl(topic) if not sub: return -1, "No subscriber for topic [%s]" % topic, 0 elif sub.has_connection(pub_uri): return 1, "_connect_topic[%s]: subscriber already connected to publisher [%s]" % ( topic, pub_uri), 0 #Negotiate with source for connection # - collect supported protocols protocols = [] for h in self.protocol_handlers: #currently only TCPROS protocols.extend(h.get_supported()) if not protocols: return 0, "ERROR: no available protocol handlers", 0 _logger.debug("connect[%s]: calling requestTopic(%s, %s, %s)", topic, caller_id, topic, str(protocols)) # 1) have to preserve original (unresolved) params as this may # go outside our graph # 2) xmlrpclib doesn't give us any way of affecting the # timeout other than affecting the global timeout. We need # to set a timeout to prevent infinite hangs. 60 seconds is # a *very* long time. All of the rospy code right now sets # individual socket timeouts, but this could potentially # affect user code. socket.setdefaulttimeout(60.) success = False interval = 0.5 # seconds # while the ROS node is not shutdown try to get the topic information # and retry on connections problems after some wait # Abort the retry if the we get a Connection Refused since at that point # we know for sure the URI is invalid while not success and not is_shutdown(): try: code, msg, result = \ xmlrpcapi(pub_uri, cache=False).requestTopic(caller_id, topic, protocols) success = True except Exception as e: if getattr(e, 'errno', None) == errno.ECONNREFUSED: code = -errno.ECONNREFUSED msg = str(e) break elif not is_shutdown(): _logger.debug("Retrying for %s" % topic) if interval < 30.0: # exponential backoff (maximum 32 seconds) interval = interval * 2 time.sleep(interval) #Create the connection (if possible) if code <= 0: _logger.debug("connect[%s]: requestTopic did not succeed %s, %s", pub_uri, code, msg) return code, msg, 0 elif not result or type(protocols) != list: return 0, "ERROR: publisher returned invalid protocol choice: %s" % ( str(result)), 0 _logger.debug("connect[%s]: requestTopic returned protocol list %s", topic, result) protocol = result[0] for h in self.protocol_handlers: if h.supports(protocol): return h.create_transport(topic, pub_uri, result) return 0, "ERROR: publisher returned unsupported protocol choice: %s" % result, 0
def _connect_topic(self, topic, pub_uri): """ Connect subscriber to topic. @param topic: Topic name to connect. @type topic: str @param pub_uri: API URI of topic publisher. @type pub_uri: str @return: [code, msg, numConnects]. numConnects is the number of subscribers connected to the topic. @rtype: [int, str, int] """ caller_id = rospy.names.get_caller_id() sub = get_topic_manager().get_subscriber_impl(topic) if not sub: return -1, "No subscriber for topic [%s]"%topic, 0 elif sub.has_connection(pub_uri): return 1, "_connect_topic[%s]: subscriber already connected to publisher [%s]"%(topic, pub_uri), 0 #Negotiate with source for connection # - collect supported protocols protocols = [] for h in self.protocol_handlers: #currently only TCPROS protocols.extend(h.get_supported()) if not protocols: return 0, "ERROR: no available protocol handlers", 0 _logger.debug("connect[%s]: calling requestTopic(%s, %s, %s)", topic, caller_id, topic, str(protocols)) # 1) have to preserve original (unresolved) params as this may # go outside our graph # 2) xmlrpclib doesn't give us any way of affecting the # timeout other than affecting the global timeout. We need # to set a timeout to prevent infinite hangs. 60 seconds is # a *very* long time. All of the rospy code right now sets # individual socket timeouts, but this could potentially # affect user code. socket.setdefaulttimeout(60.) success = False interval = 0.5 # seconds # while the ROS node is not shutdown try to get the topic information # and retry on connections problems after some wait # Abort the retry if the we get a Connection Refused since at that point # we know for sure the URI is invalid while not success and not is_shutdown(): try: code, msg, result = \ xmlrpcapi(pub_uri).requestTopic(caller_id, topic, protocols) success = True except Exception as e: if getattr(e, 'errno', None) == errno.ECONNREFUSED: code = -errno.ECONNREFUSED msg = str(e) break elif not is_shutdown(): _logger.debug("Retrying for %s" % topic) if interval < 30.0: # exponential backoff (maximum 32 seconds) interval = interval * 2 time.sleep(interval) #Create the connection (if possible) if code <= 0: _logger.debug("connect[%s]: requestTopic did not succeed %s, %s", pub_uri, code, msg) return code, msg, 0 elif not result or type(protocols) != list: return 0, "ERROR: publisher returned invalid protocol choice: %s"%(str(result)), 0 _logger.debug("connect[%s]: requestTopic returned protocol list %s", topic, result) protocol = result[0] for h in self.protocol_handlers: if h.supports(protocol): return h.create_transport(topic, pub_uri, result) return 0, "ERROR: publisher returned unsupported protocol choice: %s"%result, 0
def test_Subscriber_unregister(self): # regression test for #3029 (unregistering a Subcriber with no # callback) plus other unregistration tests import rospy from rospy.impl.registration import get_topic_manager, Registration from rospy.topics import Subscriber, DEFAULT_BUFF_SIZE #Subscriber: name, data_class, callback=None, callback_args=None, #queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False): name = 'unregistertest' rname = rospy.resolve_name(name) data_class = test_rospy.msg.Val sub = Subscriber(name, data_class) self.assertEquals(None, sub.callback) # verify impl (test_Subscriber handles more verification, we # just care about callbacks and ref_count state here) impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assert_(impl == sub.impl) self.assertEquals(1, impl.ref_count) self.assertEquals([], impl.callbacks) # unregister should release the underlying impl sub.unregister() self.assertEquals(None, get_topic_manager().get_impl(Registration.SUB, rname)) # create two subs sub2 = Subscriber(name, data_class) sub3 = Subscriber(name, data_class) impl = get_topic_manager().get_impl(Registration.SUB, rname) # - test that they share the same impl self.assert_(impl == sub2.impl) self.assert_(impl == sub3.impl) # - test basic impl state self.assertEquals([], impl.callbacks) self.assertEquals(2, impl.ref_count) sub2.unregister() self.assertEquals(1, impl.ref_count) # - make sure double unregister is safe sub2.unregister() self.assertEquals(1, impl.ref_count) # - clean it up sub3.unregister() self.assertEquals(0, impl.ref_count) self.assertEquals(None, get_topic_manager().get_impl(Registration.SUB, rname)) # CALLBACKS cb_args5 = 5 cb_args6 = 6 cb_args7 = 7 sub4 = Subscriber(name, data_class, callback1) # - use should be allowed to subcribe using the same callback # and it shouldn't interfere on unregister sub5 = Subscriber(name, data_class, callback2, cb_args5) sub6 = Subscriber(name, data_class, callback2, cb_args6) sub7 = Subscriber(name, data_class, callback2, cb_args7) impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assertEquals(4, impl.ref_count) self.assertEquals([(callback1, None), (callback2, cb_args5), (callback2, cb_args6), (callback2, cb_args7)], impl.callbacks) # unregister sub6 first to as it is most likely to confuse any callback-finding logic sub6.unregister() self.assertEquals([(callback1, None), (callback2, cb_args5), (callback2, cb_args7)], impl.callbacks) self.assertEquals(3, impl.ref_count) sub5.unregister() self.assertEquals([(callback1, None), (callback2, cb_args7)], impl.callbacks) self.assertEquals(2, impl.ref_count) sub4.unregister() self.assertEquals([(callback2, cb_args7)], impl.callbacks) self.assertEquals(1, impl.ref_count) sub7.unregister() self.assertEquals([], impl.callbacks) self.assertEquals(0, impl.ref_count) self.assertEquals(None, get_topic_manager().get_impl(Registration.SUB, rname)) # one final condition: two identical subscribers sub8 = Subscriber(name, data_class, callback1, 'hello') sub9 = Subscriber(name, data_class, callback1, 'hello') impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assertEquals([(callback1, 'hello'), (callback1, 'hello')], impl.callbacks) self.assertEquals(2, impl.ref_count) sub8.unregister() self.assertEquals([(callback1, 'hello')], impl.callbacks) self.assertEquals(1, impl.ref_count) sub9.unregister() self.assertEquals([], impl.callbacks) self.assertEquals(0, impl.ref_count)
def test_Subscriber(self): #TODO: test callback args #TODO: negative buff_size #TODO: negative queue_size import rospy from rospy.impl.registration import get_topic_manager, Registration from rospy.topics import Subscriber, DEFAULT_BUFF_SIZE #Subscriber: name, data_class, callback=None, callback_args=None, #queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False): name = 'foo' rname = rospy.resolve_name('foo') data_class = test_rospy.msg.Val # test invalid params for n in [None, '', 1]: try: Subscriber(n, data_class) self.fail("should not allow invalid name") except ValueError: pass for d in [None, 1, TestRospyTopics]: try: Subscriber(name, d) self.fail("should now allow invalid data_class") except ValueError: pass try: Subscriber(name, None) self.fail("None should not be allowed for data_class") except ValueError: pass sub = Subscriber(name, data_class) self.assertEquals(rname, sub.resolved_name) self.assertEquals(data_class, sub.data_class) self.assertEquals('test_rospy/Val', sub.type) self.assertEquals(data_class._md5sum, sub.md5sum) self.assertEquals(Registration.SUB, sub.reg_type) # verify impl as well impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assert_(impl == sub.impl) self.assertEquals([], impl.callbacks) self.assertEquals(rname, impl.resolved_name) self.assertEquals(data_class, impl.data_class) self.assertEquals(None, impl.queue_size) self.assertEquals(DEFAULT_BUFF_SIZE, impl.buff_size) self.failIf(impl.tcp_nodelay) self.assertEquals(1, impl.ref_count) self.failIf(impl.closed) # round 2, now start setting options and make sure underlying impl is reconfigured name = 'foo' data_class = test_rospy.msg.Val queue_size = 1 buff_size = 1 sub = Subscriber(name, data_class, callback=callback1, queue_size=queue_size, buff_size=buff_size, tcp_nodelay=True) self.assertEquals(rname, sub.resolved_name) # - sub.name is a backwards-compat field as it is public API self.assertEquals(rname, sub.name) self.assertEquals(data_class, sub.data_class) # verify impl impl2 = get_topic_manager().get_impl(Registration.SUB, rname) self.assert_(impl == impl2) # should be same instance self.assertEquals([(callback1, None)], impl.callbacks) self.assertEquals(rname, impl.resolved_name) self.assertEquals(data_class, impl.data_class) self.assertEquals(queue_size, impl.queue_size) self.assertEquals(buff_size, impl.buff_size) self.assert_(impl.tcp_nodelay) self.assertEquals(2, impl.ref_count) self.failIf(impl.closed) # round 3, make sure that options continue to reconfigure # underlying impl also test that tcp_nodelay is sticky. this # is technically undefined, but this is how rospy chose to # implement. name = 'foo' data_class = test_rospy.msg.Val queue_size = 2 buff_size = 2 sub = Subscriber(name, data_class, callback=callback2, queue_size=queue_size, buff_size=buff_size, tcp_nodelay=False) # verify impl impl2 = get_topic_manager().get_impl(Registration.SUB, rname) self.assert_(impl == impl2) # should be same instance self.assertEquals(set([(callback1, None), (callback2, None)]), set(impl.callbacks)) self.assertEquals(queue_size, impl.queue_size) self.assertEquals(buff_size, impl.buff_size) self.assert_(impl.tcp_nodelay) self.assertEquals(3, impl.ref_count) self.failIf(impl.closed)
def test_Publisher(self): import rospy from rospy.impl.registration import get_topic_manager, Registration from rospy.topics import Publisher, DEFAULT_BUFF_SIZE # Publisher(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None) name = 'foo' rname = rospy.resolve_name('foo') data_class = test_rospy.msg.Val # test invalid params for n in [None, '', 1]: try: Publisher(n, data_class) self.fail("should not allow invalid name") except ValueError: pass for d in [None, 1, TestRospyTopics]: try: Publisher(name, d) self.fail("should now allow invalid data_class") except ValueError: pass try: Publisher(name, None) self.fail("None should not be allowed for data_class") except ValueError: pass # round 1: test basic params pub = Publisher(name, data_class) self.assertEquals(rname, pub.resolved_name) # - pub.name is left in for backwards compatiblity, but resolved_name is preferred self.assertEquals(rname, pub.name) self.assertEquals(data_class, pub.data_class) self.assertEquals('test_rospy/Val', pub.type) self.assertEquals(data_class._md5sum, pub.md5sum) self.assertEquals(Registration.PUB, pub.reg_type) # verify impl as well impl = get_topic_manager().get_impl(Registration.PUB, rname) self.assert_(impl == pub.impl) self.assertEquals(rname, impl.resolved_name) self.assertEquals(data_class, impl.data_class) self.failIf(impl.is_latch) self.assertEquals(None, impl.latch) self.assertEquals(0, impl.seq) self.assertEquals(1, impl.ref_count) self.assertEquals('', impl.buff.getvalue()) self.failIf(impl.closed) self.failIf(impl.has_connections()) # check publish() fall-through from test_rospy.msg import Val impl.publish(Val('hello world-1')) # check stats self.assertEquals(0, impl.message_data_sent) # check acquire/release don't bomb impl.acquire() impl.release() # do a single publish with connection override. The connection # override is a major cheat as the object isn't even an actual # connection. I will need to add more integrated tests later co1 = ConnectionOverride('co1') self.failIf(impl.has_connection('co1')) impl.add_connection(co1) self.assert_(impl.has_connection('co1')) self.assert_(impl.has_connections()) impl.publish(Val('hello world-1'), connection_override=co1) import cStringIO buff = cStringIO.StringIO() Val('hello world-1').serialize(buff) # - check equals, but strip length field first self.assertEquals(co1.data[4:], buff.getvalue()) self.assertEquals(None, impl.latch) # Now enable latch pub = Publisher(name, data_class, latch=True) impl = get_topic_manager().get_impl(Registration.PUB, rname) # have to verify latching in pub impl self.assert_(impl == pub.impl) self.assertEquals(True, impl.is_latch) self.assertEquals(None, impl.latch) self.assertEquals(2, impl.ref_count) co2 = ConnectionOverride('co2') self.failIf(impl.has_connection('co2')) impl.add_connection(co2) for n in ['co1', 'co2']: self.assert_(impl.has_connection(n)) self.assert_(impl.has_connections()) v = Val('hello world-2') impl.publish(v, connection_override=co2) self.assert_(v == impl.latch) buff = cStringIO.StringIO() Val('hello world-2').serialize(buff) # - strip length and check value self.assertEquals(co2.data[4:], buff.getvalue()) # test that latched value is sent to connections on connect co3 = ConnectionOverride('co3') self.failIf(impl.has_connection('co3')) impl.add_connection(co3) for n in ['co1', 'co2', 'co3']: self.assert_(impl.has_connection(n)) self.assert_(impl.has_connections()) self.assertEquals(co3.data[4:], buff.getvalue()) # TODO: tcp_nodelay # TODO: suscribe listener self.assert_(impl.has_connection('co1')) impl.remove_connection(co1) self.failIf(impl.has_connection('co1')) self.assert_(impl.has_connections()) self.assert_(impl.has_connection('co3')) impl.remove_connection(co3) self.failIf(impl.has_connection('co3')) self.assert_(impl.has_connections()) self.assert_(impl.has_connection('co2')) impl.remove_connection(co2) self.failIf(impl.has_connection('co2')) self.failIf(impl.has_connections()) # test publish() latch on a new Publisher object (this was encountered in testing, so I want a test case for it) pub = Publisher('bar', data_class, latch=True) v = Val('no connection test') pub.impl.publish(v) self.assert_(v == pub.impl.latch) # test connection header h = {'foo': 'bar', 'fuga': 'hoge'} pub = Publisher('header_test', data_class, headers=h) self.assertEquals(h, pub.impl.headers)
def test_Publisher(self): import rospy from rospy.impl.registration import get_topic_manager, Registration from rospy.topics import Publisher, DEFAULT_BUFF_SIZE # Publisher(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None) name = 'foo' rname = rospy.resolve_name('foo') data_class = test_rospy.msg.Val # test invalid params for n in [None, '', 1]: try: Publisher(n, data_class) self.fail("should not allow invalid name") except ValueError: pass for d in [None, 1, TestRospyTopics]: try: Publisher(name, d) self.fail("should now allow invalid data_class") except ValueError: pass try: Publisher(name, None) self.fail("None should not be allowed for data_class") except ValueError: pass # round 1: test basic params pub = Publisher(name, data_class) self.assertEquals(rname, pub.resolved_name) # - pub.name is left in for backwards compatiblity, but resolved_name is preferred self.assertEquals(rname, pub.name) self.assertEquals(data_class, pub.data_class) self.assertEquals('test_rospy/Val', pub.type) self.assertEquals(data_class._md5sum, pub.md5sum) self.assertEquals(Registration.PUB, pub.reg_type) # verify impl as well impl = get_topic_manager().get_impl(Registration.PUB, rname) self.assert_(impl == pub.impl) self.assertEquals(rname, impl.resolved_name) self.assertEquals(data_class, impl.data_class) self.failIf(impl.is_latch) self.assertEquals(None, impl.latch) self.assertEquals(0, impl.seq) self.assertEquals(1, impl.ref_count) self.assertEquals('', impl.buff.getvalue()) self.failIf(impl.closed) self.failIf(impl.has_connections()) # check publish() fall-through from test_rospy.msg import Val impl.publish(Val('hello world-1')) # check stats self.assertEquals(0, impl.message_data_sent) # check acquire/release don't bomb impl.acquire() impl.release() # do a single publish with connection override. The connection # override is a major cheat as the object isn't even an actual # connection. I will need to add more integrated tests later co1 = ConnectionOverride('co1') self.failIf(impl.has_connection('co1')) impl.add_connection(co1) self.assert_(impl.has_connection('co1')) self.assert_(impl.has_connections()) impl.publish(Val('hello world-1'), connection_override=co1) import cStringIO buff = cStringIO.StringIO() Val('hello world-1').serialize(buff) # - check equals, but strip length field first self.assertEquals(co1.data[4:], buff.getvalue()) self.assertEquals(None, impl.latch) # Now enable latch pub = Publisher(name, data_class, latch=True) impl = get_topic_manager().get_impl(Registration.PUB, rname) # have to verify latching in pub impl self.assert_(impl == pub.impl) self.assertEquals(True, impl.is_latch) self.assertEquals(None, impl.latch) self.assertEquals(2, impl.ref_count) co2 = ConnectionOverride('co2') self.failIf(impl.has_connection('co2')) impl.add_connection(co2) for n in ['co1', 'co2']: self.assert_(impl.has_connection(n)) self.assert_(impl.has_connections()) v = Val('hello world-2') impl.publish(v, connection_override=co2) self.assert_(v == impl.latch) buff = cStringIO.StringIO() Val('hello world-2').serialize(buff) # - strip length and check value self.assertEquals(co2.data[4:], buff.getvalue()) # test that latched value is sent to connections on connect co3 = ConnectionOverride('co3') self.failIf(impl.has_connection('co3')) impl.add_connection(co3) for n in ['co1', 'co2', 'co3']: self.assert_(impl.has_connection(n)) self.assert_(impl.has_connections()) self.assertEquals(co3.data[4:], buff.getvalue()) # TODO: tcp_nodelay # TODO: suscribe listener self.assert_(impl.has_connection('co1')) impl.remove_connection(co1) self.failIf(impl.has_connection('co1')) self.assert_(impl.has_connections()) # TODO: need to validate DeadTransport better self.assert_( [d for d in impl.dead_connections if d.endpoint_id == 'co1']) self.assert_(impl.has_connection('co3')) impl.remove_connection(co3) self.failIf(impl.has_connection('co3')) self.assert_(impl.has_connections()) for id in ['co1', 'co3']: self.assert_( [d for d in impl.dead_connections if d.endpoint_id == id]) self.assert_(impl.has_connection('co2')) impl.remove_connection(co2) self.failIf(impl.has_connection('co2')) self.failIf(impl.has_connections()) for id in ['co1', 'co2', 'co3']: self.assert_( [d for d in impl.dead_connections if d.endpoint_id == id]) # test publish() latch on a new Publisher object (this was encountered in testing, so I want a test case for it) pub = Publisher('bar', data_class, latch=True) v = Val('no connection test') pub.impl.publish(v) self.assert_(v == pub.impl.latch) # test connection header h = {'foo': 'bar', 'fuga': 'hoge'} pub = Publisher('header_test', data_class, headers=h) self.assertEquals(h, pub.impl.headers)
def test_Subscriber_unregister(self): # regression test for #3029 (unregistering a Subcriber with no # callback) plus other unregistration tests import rospy from rospy.impl.registration import get_topic_manager, Registration from rospy.topics import Subscriber, DEFAULT_BUFF_SIZE #Subscriber: name, data_class, callback=None, callback_args=None, #queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False): name = 'unregistertest' rname = rospy.resolve_name(name) data_class = test_rospy.msg.Val sub = Subscriber(name, data_class) self.assertEquals(None, sub.callback) # verify impl (test_Subscriber handles more verification, we # just care about callbacks and ref_count state here) impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assert_(impl == sub.impl) self.assertEquals(1, impl.ref_count) self.assertEquals([], impl.callbacks) # unregister should release the underlying impl sub.unregister() self.assertEquals( None, get_topic_manager().get_impl(Registration.SUB, rname)) # create two subs sub2 = Subscriber(name, data_class) sub3 = Subscriber(name, data_class) impl = get_topic_manager().get_impl(Registration.SUB, rname) # - test that they share the same impl self.assert_(impl == sub2.impl) self.assert_(impl == sub3.impl) # - test basic impl state self.assertEquals([], impl.callbacks) self.assertEquals(2, impl.ref_count) sub2.unregister() self.assertEquals(1, impl.ref_count) # - make sure double unregister is safe sub2.unregister() self.assertEquals(1, impl.ref_count) # - clean it up sub3.unregister() self.assertEquals(0, impl.ref_count) self.assertEquals( None, get_topic_manager().get_impl(Registration.SUB, rname)) # CALLBACKS cb_args5 = 5 cb_args6 = 6 cb_args7 = 7 sub4 = Subscriber(name, data_class, callback1) # - use should be allowed to subcribe using the same callback # and it shouldn't interfere on unregister sub5 = Subscriber(name, data_class, callback2, cb_args5) sub6 = Subscriber(name, data_class, callback2, cb_args6) sub7 = Subscriber(name, data_class, callback2, cb_args7) impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assertEquals(4, impl.ref_count) self.assertEquals([(callback1, None), (callback2, cb_args5), (callback2, cb_args6), (callback2, cb_args7)], impl.callbacks) # unregister sub6 first to as it is most likely to confuse any callback-finding logic sub6.unregister() self.assertEquals([(callback1, None), (callback2, cb_args5), (callback2, cb_args7)], impl.callbacks) self.assertEquals(3, impl.ref_count) sub5.unregister() self.assertEquals([(callback1, None), (callback2, cb_args7)], impl.callbacks) self.assertEquals(2, impl.ref_count) sub4.unregister() self.assertEquals([(callback2, cb_args7)], impl.callbacks) self.assertEquals(1, impl.ref_count) sub7.unregister() self.assertEquals([], impl.callbacks) self.assertEquals(0, impl.ref_count) self.assertEquals( None, get_topic_manager().get_impl(Registration.SUB, rname)) # one final condition: two identical subscribers sub8 = Subscriber(name, data_class, callback1, 'hello') sub9 = Subscriber(name, data_class, callback1, 'hello') impl = get_topic_manager().get_impl(Registration.SUB, rname) self.assertEquals([(callback1, 'hello'), (callback1, 'hello')], impl.callbacks) self.assertEquals(2, impl.ref_count) sub8.unregister() self.assertEquals([(callback1, 'hello')], impl.callbacks) self.assertEquals(1, impl.ref_count) sub9.unregister() self.assertEquals([], impl.callbacks) self.assertEquals(0, impl.ref_count)