# Software License Agreement (BSD License)
#
# Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Fraunhofer nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import roslib
import rospy
from .common import get_hostname
from .filter_interface import FilterInterface
[docs]class NodeInfo(object):
'''
The NodeInfo class stores informations about a ROS node.
:param name: the name of the node
:type name: str
:param masteruri: the URI of the ROS master, where the node is registered.
This masteruri will be used to determine, whether the ROS master and the
node are running on the same machine.
:type masteruri: str
'''
def __init__(self, name, masteruri):
'''
Creates a new NodeInfo for a node with given name.
:param name: the name of the node
:type name: str
:param masteruri: the URI of the ROS master, where the node is registered.
This masteruri will be used to determine, whether the ROS master and the
node are running on the same machine.
:type masteruri: str
'''
self.__name = name
self.__masteruri = masteruri
self.__org_masteruri = masteruri
self.__uri = None
self.pid = None
'''the process id of the node. Invalid id has a ``None`` value'''
self.__local = False
self.__local_master = True
self._publishedTopics = []
self._subscribedTopics = []
self._services = []
@property
[docs] def name(self):
'''
:return: the name of the node.
:rtype: str
'''
return self.__name
@property
def uri(self):
'''
:return: the URI of the RPC API of the node.
:rtype: str
'''
return self.__uri
@uri.setter
[docs] def uri(self, uri):
'''
Sets the URI of the RPC API of the node.
'''
self.__uri = uri
self.__local = NodeInfo.local_(self.__masteruri, self.__org_masteruri, self.__uri)
@property
def masteruri(self):
'''
:return: the URI of the ROS master where the node is registered.
:rtype: str
'''
return self.__org_masteruri
@masteruri.setter
[docs] def masteruri(self, uri):
'''
Sets the ROS master URI.
'''
self.__org_masteruri = uri
self.__local = NodeInfo.local_(self.__masteruri, self.__org_masteruri, self.__uri)
self.__local_master = (self.__masteruri == self.__org_masteruri)
@property
[docs] def isLocal(self):
'''
:return: ``True`` if the node and the ROS master are running on the same machine.
:rtype: bool
'''
return self.__local
@property
[docs] def isLocalMaster(self):
'''
:return: ``True`` if the node is registered on the local machine.
:rtype: bool
'''
return self.__local_master
@property
def publishedTopics(self):
'''
:return: the list of all published topics by this node.
:rtype: list of strings
'''
return self._publishedTopics
@publishedTopics.setter
[docs] def publishedTopics(self, name):
'''
Append a new published topic to this node.
:param name: the name of the topic
:type name: str
'''
try:
if isinstance(name, list):
del self._publishedTopics
self._publishedTopics = name
else:
self._publishedTopics.index(name)
except ValueError:
self._publishedTopics.append(name)
# @publishedTopics.deleter
# def publishedTopics(self):
# del self._publishedTopics
@property
def subscribedTopics(self):
'''
:return: the list of all subscribed topics by this node.
:rtype: list of strings
'''
return self._subscribedTopics
@subscribedTopics.setter
[docs] def subscribedTopics(self, name):
'''
Append a new subscribed topic to this node.
:param name: the name of the topic
:type name: str
'''
try:
if isinstance(name, list):
del self._subscribedTopics
self._subscribedTopics = name
else:
self._subscribedTopics.index(name)
except ValueError:
self._subscribedTopics.append(name)
# @subscribedTopics.deleter
# def subscribedTopics(self):
# del self._subscribedTopics
@property
def services(self):
'''
:return: the list of all services provided by this node.
:rtype: list of strings
'''
return self._services
@services.setter
[docs] def services(self, name):
'''
Append a new service to this node.
:param name: the name of the topic
:type name: str
'''
try:
if isinstance(name, list):
del self._services
self._services = name
else:
self._services.index(name)
except ValueError:
self._services.append(name)
# @services.deleter
# def services(self):
# del self._services
[docs] def copy(self, new_masteruri=None):
'''
Creates a copy of this object and returns it.
:param new_masteruri: the masteruri of the new masterinfo
:rtype: :mod:`master_discovery_fkie.master_info.NodeInfo`
'''
if new_masteruri is None:
new_masteruri = self.masteruri
result = NodeInfo(self.name, new_masteruri)
result.uri = str(self.uri) if self.uri is not None else None
result.masteruri = self.masteruri
result.pid = self.pid
result._publishedTopics = list(self._publishedTopics)
result._subscribedTopics = list(self._subscribedTopics)
result._services = list(self._services)
return result
@staticmethod
[docs] def local_(masteruri, org_masteruri, uri):
'''
Test the node whether it's run on the same machineas the ROS master and ``masteruri`` and ``org_masteruri`` are equal.
:param masteruri: The URI of the ROS master currently tested.
:type masteruri: str
:param org_masteruri: The URI of the ROS master, where the node was originally registered.
:type org_masteruri: str
:param uri: The URI of the node.
:type uri: str
:rtype: bool
'''
result = False
try:
om = get_hostname(masteruri)
on = get_hostname(uri)
result = (om == on) and (masteruri == org_masteruri)
except:
pass
return result
[docs]class TopicInfo(object):
'''
The TopicInfo class stores informations about a ROS topic.
:param name: the name of the topic
:type name: str
'''
def __init__(self, name):
'''
Creates a new TopicInfo for a topic with given name.
:param name: the name of the topic
:type name: str
'''
self.__name = name
self.type = None
'''the type of the topic. (Default: ``None``)'''
self._publisherNodes = []
self._subscriberNodes = []
@property
[docs] def name(self):
'''
:return: the name of the topic.
:rtype: str
'''
return self.__name
@property
def publisherNodes(self):
'''
:return: the list with node names witch are publishing to this topic.
:rtype: list of strings
'''
return list(self._publisherNodes)
@publisherNodes.setter
[docs] def publisherNodes(self, name):
'''
Append a new publishing node to this topic.
'''
try:
if isinstance(name, list):
del self._publisherNodes
self._publisherNodes = name
else:
self._publisherNodes.index(name)
except ValueError:
self._publisherNodes.append(name)
# @publisherNodes.deleter
# def publisherNodes(self):
# del self._publisherNodes
@property
def subscriberNodes(self):
'''
:return: the list with node names witch are subscribed to this topic.
:rtype: list of strings
'''
return list(self._subscriberNodes)
@subscriberNodes.setter
[docs] def subscriberNodes(self, name):
'''
Append a new subscribing node to this topic.
'''
try:
if isinstance(name, list):
del self._subscriberNodes
self._subscriberNodes = name
else:
self._subscriberNodes.index(name)
except ValueError:
self._subscriberNodes.append(name)
# @subscriberNodes.deleter
# def subscriberNodes(self):
# del self._subscriberNodes
[docs] def copy(self):
'''
Creates a copy this object and returns it.
:rtype: :mod:`master_discovery_fkie.master_info.TopicInfo`
'''
result = TopicInfo(self.name)
result.type = self.type
result._publisherNodes = list(self._publisherNodes)
result._subscriberNodes = list(self._subscriberNodes)
return result
[docs]class ServiceInfo(object):
'''
The ServiceInfo class stores informations about a ROS service.
:param name: the name of the service
:type name: str
:param masteruri: the URI of the ROS master, where the service is registered.
This masteruri will be used to determine, whether the ROS master and the
service are running on the same machine.
:type masteruri: str
'''
def __init__(self, name, masteruri):
'''
Creates a new instance of the ServiceInfo.
:param name: the name of the service
:type name: str
:param masteruri: the URI of the ROS master, where the service is registered.
This masteruri will be used to determine, whether the ROS master and the
service are running on the same machine.
:type masteruri: str
'''
self.__name = name
self.__masteruri = masteruri
self.__org_masteruri = masteruri
self.__uri = None
self.__local = False
self.__local_master = True
self.type = None
'''the type of the service. (Default: ``None``)'''
self.__service_class = None
self.args = None
self._serviceProvider = []
@property
[docs] def name(self):
'''
:return: the name of the service.
:rtype: str
'''
return self.__name
@property
def uri(self):
'''
:return: the URI of the RPC API of the service
:rtype: str
'''
return self.__uri
@uri.setter
[docs] def uri(self, uri):
'''
Sets the uri of the service RPC interface and determine whether this service
and the ROS master are running on the same machine.
:param uri: The URI of the service RPC interface
:type uri: str
'''
self.__uri = uri
self.__local = NodeInfo.local_(self.__masteruri, self.__org_masteruri, self.__uri)
@property
def masteruri(self):
'''
:return: the URI of the ROS master of the service
:rtype: str
'''
return self.__org_masteruri
@masteruri.setter
[docs] def masteruri(self, uri):
'''
Sets the uri of the origin ROS master and determine whether this service
and the ROS master are running on the same machine.
:param uri: The URI of the ROS master
:type uri: str
'''
self.__org_masteruri = uri
self.__local_master = (self.__masteruri == self.__org_masteruri)
self.__local = NodeInfo.local_(self.__masteruri, self.__org_masteruri, self.__uri)
@property
[docs] def isLocal(self):
'''
:return: `True`, if this service and the master are on the same machine.
This will be determine on setting the uri-parameter.
:rtype: bool
'''
return self.__local
@property
[docs] def isLocalMaster(self):
'''
:return: ``True`` if the service is registered on the local machine.
:rtype: bool
'''
return self.__local_master
@property
def serviceProvider(self):
'''
:return: the list of the node names, which provide this service.
:rtype: list of strings
'''
return self._serviceProvider
@serviceProvider.setter
def serviceProvider(self, name):
'''
Adds a new service provider, if no one with given name exists.
:param name: name of the new service provider
:type name: str
'''
try:
self._serviceProvider.index(name)
except ValueError:
self._serviceProvider.append(name)
@serviceProvider.deleter
[docs] def serviceProvider(self):
del self._serviceProvider
[docs] def get_service_class(self, allow_get_type=False):
'''
Get the service class using the type of the service. NOTE: this
method is from `rosservice` and changed to avoid a probe call to the service.
:param allow_get_type: allow to connect to service and get the type if the type is not valid (in case of other host)
:type allow_get_type: bool
:return: service class
:rtype: ServiceDefinition: service class
:raise: ``ROSServiceException``, if service class cannot be retrieved
'''
if self.__service_class is not None:
return self.__service_class
srv_type = self.type
# request the type if it is empty and allowed
if not srv_type and allow_get_type and self.uri:
dest_addr = dest_port = None
try:
dest_addr, dest_port = rospy.parse_rosrpc_uri(self.uri)
except:
pass
else:
import socket
import cStringIO
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# connect to service and probe it to get the headers
s.settimeout(0.5)
s.connect((dest_addr, dest_port))
header = {'probe': '1', 'md5sum': '*',
'callerid': rospy.get_name(), 'service': self.name}
roslib.network.write_ros_handshake_header(s, header)
srv_type = roslib.network.read_ros_handshake_header(s, cStringIO.StringIO(), 2048)
srv_type = srv_type['type']
except socket.error:
pass
except:
pass
finally:
if s is not None:
s.close()
import rosservice
if not srv_type:
raise rosservice.ROSServiceException("Not valid type of service [%s]." % str(srv_type))
# get the Service class so we can populate the request
service_class = roslib.message.get_service_class(srv_type)
# #1083: roscpp services are currently returning the wrong type
if service_class and self.type.endswith('Request') and \
not hasattr(service_class, "_request_class"):
srv_type = srv_type[:-7]
service_class = roslib.message.get_service_class(srv_type)
if service_class is None:
pkg = roslib.names.resource_name_package(self.type)
raise rosservice.ROSServiceException("Unable to load type [%s].\n" % self.type +
"Have you typed 'make' in [%s]?" % pkg)
self.__service_class = service_class
return service_class
[docs] def copy(self, new_masteruri=None):
'''
Creates a copy of this object and returns it.
:param new_masteruri: the masteruri of the new masterinfo
:rtype: :mod:`master_discovery_fkie.master_info.NodeInfo`
'''
if new_masteruri is None:
new_masteruri = self.masteruri
result = ServiceInfo(self.name, new_masteruri)
result.uri = self.uri
result.masteruri = self.masteruri
result.type = self.type
result.args = self.args
result._serviceProvider = list(self.serviceProvider)
return result
[docs]class MasterInfo(object):
'''
The MasterInfo class stores informations about a ROS master.
Not thread safe!
:param masteruri: The URI of the corresponding master
:type masteruri: str
:param mastername: The name of the ROS master. If no one is given, it will be
extracted from the masteruri.
:type mastername: str or ``None`` (Default: ``None``)
'''
def __init__(self, masteruri, mastername=None):
'''
Creates a new instance of the MasterInfo. The mastername will be extracted
from the masterui, if no name is given.
:param masteruri: The URI of the corresponding master
:type masteruri: str
:param mastername: The name of the ROS master. If no one is given, it will be
extracted from the masteruri.
:type mastername: str or ``None`` (Default: ``None``)
'''
self.__masteruri = masteruri
self.__mastername = mastername
if mastername is None:
self.__mastername = get_hostname(self.__masteruri)
self.__nodelist = {}
self.__topiclist = {}
self.__servicelist = {}
self.__timestamp = 0
self.__timestamp_local = 0
self.check_ts = 0
'''the last time, when the state of the ROS master retrieved'''
@staticmethod
[docs] def from_list(l):
'''
Creates a new instance of the MasterInfo from given list.
:param l: the list returned by :mod:`master_discovery_fkie.master_info.MasterInfo.listedState()`
:type l: list
:return: the new instance of the MasterInfo filled from list.
:rtype: :mod:`master_discovery_fkie.master_info.MasterInfo`
'''
if l is None:
return None
result = MasterInfo(l[2], l[3])
result.timestamp = float(l[0])
result.timestamp_local = float(l[1])
publishers = l[4]
subscribers = l[5]
services = l[6]
topicTypes = l[7]
nodes = l[8]
serviceProvider = l[9]
# set the publishers
for pub, nodelist in publishers:
result.topics = pub
for n in nodelist:
result.nodes = n
result.getNode(n).publishedTopics = pub
result.getTopic(pub).publisherNodes = n
# set the subscribers
for sub, nodelist in subscribers:
result.topics = sub
for n in nodelist:
result.nodes = n
result.getNode(n).subscribedTopics = sub
result.getTopic(sub).subscriberNodes = n
# set the services
for s, provider in services:
result.services = s
for n in provider:
result.nodes = n
result.getNode(n).services = s
result.getService(s).serviceProvider = n
# set the topic types
for topic, ttype in topicTypes:
result.topics = topic
result.getTopic(topic).type = ttype
# set the node informations
for nodename, uri, masteruri, pid, _local in nodes:
result.nodes = nodename
result.getNode(nodename).uri = uri
result.getNode(nodename).masteruri = masteruri
result.getNode(nodename).pid = pid
# set the service informations
for servicename, uri, masteruri, stype, _local in serviceProvider:
result.services = servicename
result.getService(servicename).uri = uri
result.getService(servicename).masteruri = masteruri
result.getService(servicename).type = stype
return result
@property
[docs] def mastername(self):
'''
:return: the name of the ROS master. In most cases the ROS master name is the
name of the host, where the ROS master running. Although it can differ.
:rtype: str
'''
return self.__mastername
@property
[docs] def masteruri(self):
'''
:return: the URI of the ROS master.
:rtype: str
'''
return self.__masteruri
@property
def timestamp(self):
'''
:return: The timestamp when this MasterInfo was first time filled with the
information. See :mod:`master_discovery_fkie.master_info.MasterInfo.check_ts()`
to get the time, when the information was compared with the data of ROS Master.
:rtype: float
'''
return self.__timestamp
@timestamp.setter
[docs] def timestamp(self, ts):
'''
Sets the timestamp of this instance
:param ts: the new timestamp
:type ts: float
'''
self.__timestamp = ts
self.check_ts = ts
self.__timestamp_local = ts
@property
def timestamp_local(self):
'''
:return: The timestamp when this MasterInfo was first time filled with the
information. See :mod:`master_discovery_fkie.master_info.MasterInfo.check_ts()`
to get the time, when the information was compared with the data of ROS Master.
This timestamp is only updated, not synchronized nodes, topics or services are
changed.
:rtype: float
'''
return self.__timestamp_local
@timestamp_local.setter
[docs] def timestamp_local(self, ts):
'''
Sets the timestamp of this instance
:param ts: the new timestamp
:type ts: float
'''
self.__timestamp_local = ts
@property
def nodes(self):
'''
:return: the dictionary with ``node names`` and corresponding instances of ``NodeInfo``.
:rtype: dict of (str : :mod:`master_discovery_fkie.master_info.NodeInfo`)
'''
return self.__nodelist
@nodes.setter
[docs] def nodes(self, name):
'''
Adds a new :mod:`master_discovery_fkie.master_info.NodeInfo` with given name.
:note: If the NodeInfo already exists, do nothing.
:param name: the name of new :mod:`master_discovery_fkie.master_info.NodeInfo`
:type name: str
'''
if (name is None) or not name:
return None
if not (name in self.__nodelist):
self.__nodelist[name] = NodeInfo(name, self.__masteruri)
@property
[docs] def node_names(self):
'''
:return: the list with node names
:rtype: list of strings
'''
# @return: the list with node names
return self.__nodelist.keys()
@property
[docs] def node_uris(self):
'''
:return: the list with node URI's.
:rtype: list of strings
'''
uris = []
for node in self.__nodelist.itervalues():
uris.append(node.uri)
return uris
@property
def topics(self):
'''
:return: the dictionary with ``topic names`` and corresponding ``TopicInfo`` instances.
:rtype: dict of (str : :mod:`master_discovery_fkie.master_info.TopicInfo`)
'''
return self.__topiclist
@topics.setter
[docs] def topics(self, name):
'''
Adds a new TopicInfo with given name. If the ``TopicInfo`` already exists, do
nothing.
:param name: the name of new :mod:`master_discovery_fkie.master_info.TopicInfo`
:type name: str
'''
if (name is None) or not name:
return None
if not (name in self.__topiclist):
self.__topiclist[name] = TopicInfo(name)
@property
[docs] def topic_names(self):
'''
:return: the list with topic names.
:rtype: list of strings
'''
return self.__topiclist.keys()
@property
def services(self):
'''
:return: the dictionary with ``service names`` and corresponding ``ServiceInfo`` instances.
:rtype: dict of (str : :mod:`master_discovery_fkie.master_info.ServiceInfo`)
'''
return self.__servicelist
@services.setter
[docs] def services(self, name):
'''
Adds a new :mod:`master_discovery_fkie.master_info.ServiceInfo` with given name. If the ServiceInfo already exists, do
nothing.
:param name: the name of new :mod:`master_discovery_fkie.master_info.ServiceInfo`
:type name: str
'''
if (name is None) or not name:
return None
if not (name in self.__servicelist):
self.__servicelist[name] = ServiceInfo(name, self.__masteruri)
@property
[docs] def service_names(self):
'''
:return: the list with service names.
:rtype: list of strings
'''
return self.__servicelist.keys()
@property
[docs] def service_uris(self):
'''
:return: the list with service URI's.
:rtype: list of strings
'''
uris = []
for service in self.__servicelist.itervalues():
uris.append(service.uri)
return uris
[docs] def getNode(self, name):
'''
:param name: the name of the node
:type name: str
:return: the instance of the :mod:`master_discovery_fkie.master_info.NodeInfo` with given name
:rtype: :mod:`master_discovery_fkie.master_info.NodeInfo` or ``None``
'''
if (name is None) or not name:
return None
return self.__nodelist.get(name, None)
[docs] def getNodeEndsWith(self, suffix):
'''
Returns the node, which name ends with given suffix. On more then one node, only the fist found will be returned.
:param suffix: the end of the name
:type suffix: str
:return: the instance of the :mod:`master_discovery_fkie.master_info.NodeInfo` with with given suffix
:rtype: :mod:`master_discovery_fkie.master_info.NodeInfo` or ``None``
'''
if (suffix is None) or not suffix:
return None
for name, node in self.__nodelist.items():
if name.endswith(suffix):
return node
return None
[docs] def getTopic(self, name):
'''
Returns the topics with given name.
:param name: the name of the topic
:type name: str
:return: the instance of the :mod:`master_discovery_fkie.master_info.TopicInfo` with given name.
:rtype: :mod:`master_discovery_fkie.master_info.TopicInfo` or ``None``
'''
if (name is None) or not name:
return None
return self.__topiclist.get(name, None)
[docs] def getService(self, name):
'''
Returns the service with given name.
:param name: the name of the service
:type name: str
:return: the instance of the :mod:`master_discovery_fkie.master_info.ServiceInfo` with given name
:rtype: :mod:`master_discovery_fkie.master_info.ServiceInfo` or ``None``
'''
if (name is None) or not name:
return None
return self.__servicelist.get(name, None)
def __eq__(self, other):
'''
Compares the master state with other master state. The timestamp will not be
compared.
:param other: the another MasterInfo instance.
:type other: :mod:`master_discovery_fkie.master_info.MasterInfo`
:return: ``True``, if the states are equal.
:rtype: boolean
'''
# import os ###################
# cputimes = os.times() ###################
# cputime_init = cputimes[0] + cputimes[1] ###################
# try:
if (other is None):
return False
if (self.masteruri != other.masteruri):
return False
if (set(self.node_uris) ^ set(other.node_uris)):
return False
# if (set(self.node_names) ^ set(other.node_names)):
# return False
# if (set(self.service_names) ^ set(other.service_names)):
# return False
if (set(self.service_uris) ^ set(other.service_uris)):
return False
if (set(self.topic_names) ^ set(other.topic_names)):
return False
# test for changes of each node parameter
for name in self.node_names:
n1 = self.getNode(name)
n2 = other.getNode(name)
if n1 is not None and n2 is not None:
if n1.pid != n2.pid:
return False
# if n1.uri != n2.uri:
# return False
if set(n1.publishedTopics) ^ set(n2.publishedTopics):
return False
if set(n1.subscribedTopics) ^ set(n2.subscribedTopics):
return False
if set(n1.services) ^ set(n2.services):
return False
return True
# finally:
# cputimes = os.times() ###################
# print "EQ:", (cputimes[0] + cputimes[1] - cputime_init), ", count nodes:", len(self.node_names) ###################
def __ne__(self, other):
return not self.__eq__(other)
[docs] def has_local_changes(self, other):
'''
Compares the master state with other master state. The timestamp will not be
compared.
:param other: the another ``MasterInfo`` instance.
:type other: :mod:`master_discovery_fkie.master_info.MasterInfo`
:return: a tupel with two boolean values (all equal, only local equal)
:rtype: (bool, bool)
'''
# import os ###################
# cputimes = os.times() ###################
# cputime_init = cputimes[0] + cputimes[1] ###################
# try:
if (other is None):
return True
if (self.masteruri != other.masteruri):
return True
# test for nodes
node_names = list((set(self.node_names) | set(other.node_names)))
for name in node_names:
n1 = self.getNode(name)
n2 = other.getNode(name)
if n1 is not None and n2 is not None:
if n1.isLocal or n1.isLocalMaster:
if n1.pid != n2.pid:
return True
if n1.uri != n2.uri:
return True
if set(n1.publishedTopics) ^ set(n2.publishedTopics):
return True
if set(n1.subscribedTopics) ^ set(n2.subscribedTopics):
return True
if set(n1.services) ^ set(n2.services):
return True
# after local start of asynchronized node. The next check of master state return the local node.
elif n2.isLocal or n2.isLocalMaster:
return True
elif n1 is not None:
if n1.isLocal or n1.isLocalMaster:
return True
elif n2 is not None:
if n2.isLocal or n2.isLocalMaster:
return True
# test for services
service_names = list(set(self.service_uris) | set(other.service_uris))
for name in service_names:
s1 = self.getService(name)
s2 = other.getService(name)
if s1 is not None and s2 is not None:
if s1.isLocal or s1.isLocalMaster:
if s1.uri != s2.uri:
return True
elif s2.isLocal or s2.isLocalMaster:
return True
elif s1 is not None:
if s1.isLocal or s1.isLocalMaster:
return True
elif s2 is not None:
if s2.isLocal or s2.isLocalMaster:
return True
return False
# finally:
# cputimes = os.times() ###################
# print "CHANGES:", (cputimes[0] + cputimes[1] - cputime_init), ", count nodes:", len(self.node_names) ###################
[docs] def listedState(self, filter_interface=None):
'''
Returns a extended ROS Master State.
:param filter_interface: The filter used to filter the nodes, topics or serivces out.
:type filter_interface: FilterInterface
:return: complete ROS Master State as
``(stamp, stamp_local, masteruri, name, publishers, subscribers, services, topicTypes, nodes, serviceProvider)``
- ``publishers`` is of the form
``[ [topic1, [topic1Publisher1...topic1PublisherN]] ... ]``
- ``subscribers`` is of the form
``[ [topic1, [topic1Subscriber1...topic1SubscriberN]] ... ]``
- ``services`` is of the form
``[ [service1, [service1Provider1...service1ProviderN]] ... ]``
- ``topicTypes`` is a list of
``[ [topicName1, topicType1], ... ]``
- ``nodes`` is a list of (the pid of remote Nodes will not be resolved)
``[nodename, XML-RPC URI, origin ROS_MASTER_URI, pid, {local, remote}]``
- ``serviceProvider`` is a list of (the type, serviceClass and args of remote Services will not be resolved)
``[service, XML-RPC URI, origin ROS_MASTER_URI, type, {local, remote}]``
:rtype: (``float``,
``float``,
``str``,
``str``,
``[ [str,[str] ] ]``,
``[ [str,[str] ] ]``,
``[ [str,[str] ] ]``,
``[ [str,str] ]``,
``[ [str,str,str,int,str] ]``,
``[ [str,str,str,str,str] ])``
'''
iffilter = filter_interface
if iffilter is None:
iffilter = FilterInterface.from_list()
stamp = '%.9f' % self.timestamp
stamp_local = '%.9f' % self.timestamp_local
publishers = []
subscribers = []
services = []
topicTypes = []
nodes = []
serviceProvider = []
added_nodes = []
nodes_last_check = set()
# process the node filtering first, but the nodelist to send will be created later
for name, node in self.nodes.items():
if not iffilter.is_ignored_node(name):
if filter_interface is None or node.isLocal or (iffilter.sync_remote_nodes() and self.masteruri == str(node.masteruri)):
added_nodes.append(name)
# filter the topics
for name, topic in self.topics.items():
pn = []
for n in topic.publisherNodes:
if n in added_nodes:
if not iffilter.is_ignored_publisher(n, name, topic.type):
pn.append(n)
nodes_last_check.add(n)
if pn:
publishers.append((name, pn))
sn = []
for n in topic.subscriberNodes:
if n in added_nodes:
if not iffilter.is_ignored_subscriber(n, name, topic.type):
sn.append(n)
nodes_last_check.add(n)
if sn:
subscribers.append((name, sn))
if pn or sn:
topicTypes.append((name, topic.type))
# filter the services
for name, service in self.services.items():
srv_prov = []
for sp in service.serviceProvider:
if sp in added_nodes:
if not iffilter.is_ignored_service(sp, name):
srv_prov.append(sp)
nodes_last_check.add(sp)
if srv_prov:
services.append((name, srv_prov))
serviceProvider.append((name, service.uri, str(service.masteruri), service.type if service.type is not None else '', 'local' if service.isLocal else 'remote'))
# creates the nodes list
for name, node in self.nodes.items():
if name in nodes_last_check:
nodes.append((name, node.uri, str(node.masteruri), node.pid, 'local' if node.isLocal else 'remote'))
return (stamp, stamp_local, self.masteruri, self.mastername, publishers, subscribers, services, topicTypes, nodes, serviceProvider)
# def __str__(self):
# return str(self.listedState())
[docs] def updateInfo(self, other):
'''
Updates the information about nodes, topics and services. If the other
masterinfo is from the same ROS Master all informations are copied. If other
contains the info from remote ROS Master, only the informations for
synchronized nodes, topics or services are copied.
:param other: the new master information object
:type other: MasterInfo
:return: The tuple of sets with added, changed and removed nodes, topics and services
:rtype: (nodes_added, nodes_changed, nodes_removed, topics_added, topics_changed, topics_removed, services_added, services_changed, services_removed)
'''
if other is None:
return
topics_added = set()
topics_changed = set()
topics_removed = set()
local_info = (self.masteruri == other.masteruri)
if local_info:
self.timestamp = other.timestamp
self.timestamp_local = other.timestamp_local
self.check_ts = other.check_ts
# update topics
own_topics_set = set(self.__topiclist.keys())
other_topics_set = set(other.__topiclist.keys())
topics_removed = own_topics_set - other_topics_set
for t in topics_removed:
del self.__topiclist[t]
common_topics = own_topics_set & other_topics_set
for t in common_topics:
own_topic = self.__topiclist[t]
other_topic = other.__topiclist[t]
if own_topic.type != other_topic.type:
topics_changed.add(t)
own_topic.type = other_topic.type
if set(own_topic._publisherNodes) ^ set(other_topic.publisherNodes):
topics_changed.add(t)
own_topic._publisherNodes = other_topic.publisherNodes
if set(own_topic._subscriberNodes) ^ set(other_topic.subscriberNodes):
topics_changed.add(t)
own_topic._subscriberNodes = other_topic.subscriberNodes
topics_added = other_topics_set - own_topics_set
for t in topics_added:
self.__topiclist[t] = other.__topiclist[t]
nodes_changed = set()
services_changed = set()
# UPDATE NODES
# get local nodes from other
own_remote_nodes = dict()
other_local_nodes = dict()
for nodename, n in self.nodes.items():
if n.masteruri == other.masteruri or self.masteruri == other.masteruri:
own_remote_nodes[nodename] = n
for nodename, n in other.nodes.items():
if n.isLocal or self.masteruri == other.masteruri:
other_local_nodes[nodename] = n
# remove nodes
own_remote_nodes_set = set(own_remote_nodes.keys())
other_local_nodes_set = set(other_local_nodes.keys())
nodes2remove = own_remote_nodes_set - other_local_nodes_set
if nodes2remove:
if local_info:
for n in nodes2remove:
del self.__nodelist[n]
else:
pass
# if the node is in own master_info, but not in remote, replace only the masteruri.
# perhaps, if will be removed soon by master_sync
# for n in nodes2remove:
# own_remote_nodes[n].masteruri = self.masteruri
# update nodes
nodes2update = own_remote_nodes_set & other_local_nodes_set
if nodes2update:
for n in nodes2update:
own_node = own_remote_nodes[n]
other_node = other_local_nodes[n]
if other_node.isLocal:
if own_node.pid != other_node.pid:
nodes_changed.add(n)
own_node.pid = other_node.pid
if own_node.uri != other_node.uri:
nodes_changed.add(n)
own_node.uri = other_node.uri
# update subscriptions of nodes
if set(own_node._publishedTopics) ^ set(other_node.publishedTopics):
nodes_changed.add(n)
own_node._publishedTopics = other_node.publishedTopics
if set(own_node._subscribedTopics) ^ set(other_node.subscribedTopics):
nodes_changed.add(n)
own_node._subscribedTopics = other_node.subscribedTopics
if set(own_node._services) ^ set(other_node.services):
nodes_changed.add(n)
own_node._services = other_node.services
# add new nodes
nodes_added = set()
if local_info:
nodes2add = other_local_nodes_set - own_remote_nodes_set
if nodes2add:
for n in nodes2add:
if not (n in self.__nodelist):
nodes_added.add(n)
self.__nodelist[n] = other_local_nodes[n].copy(self.masteruri)
# UPDATE SERVICES
own_remote_srvs = dict()
other_local_srvs = dict()
for srvname, s in self.services.items():
if s.masteruri == other.masteruri or self.masteruri == other.masteruri:
own_remote_srvs[srvname] = s
for srvname, s in other.services.items():
if s.isLocal or self.masteruri == other.masteruri:
other_local_srvs[srvname] = s
own_remote_srvs_set = set(own_remote_srvs.keys())
other_local_srvs_set = set(other_local_srvs.keys())
# remove services
srvs2remove = own_remote_srvs_set - other_local_srvs_set
if srvs2remove:
if local_info:
for s in srvs2remove:
del self.__servicelist[s]
else:
# if the service is in own master_info, but not in remote, replace only the masteruri.
# perhaps, if will be removed soon by master_sync
for s in srvs2remove:
own_remote_srvs[s].masteruri = self.masteruri
# update services
srv2update = own_remote_srvs_set & other_local_srvs_set
if srv2update:
for s in srv2update:
own_srv = own_remote_srvs[s]
other_srv = other_local_srvs[s]
if other_srv.isLocal:
if own_srv.type != other_srv.type:
services_changed.add(s)
own_srv.type = other_srv.type
if own_srv.uri != other_srv.uri:
services_changed.add(s)
own_srv.uri = other_srv.uri
if own_srv.args != other_srv.args:
services_changed.add(s)
own_srv.args = other_srv.args
# update provider
if set(own_srv._serviceProvider) ^ set(other_srv.serviceProvider):
services_changed.add(s)
own_srv._serviceProvider = other_srv.serviceProvider
# add new services
srvs_added = set()
if local_info:
srv2add = other_local_srvs_set - own_remote_srvs_set
if srv2add:
for s in srv2add:
if not (s in self.__servicelist):
srvs_added.add(s)
self.__servicelist[s] = other_local_srvs[s].copy(self.masteruri)
return (nodes_added, nodes_changed, nodes2remove, topics_added, topics_changed, topics_removed, srvs_added, services_changed, srvs2remove)