to ros_comm
This commit is contained in:
parent
30c9d18c47
commit
f4ca3d1372
|
@ -1,30 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.4.6)
|
||||
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
||||
|
||||
# Set the build type. Options are:
|
||||
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
|
||||
# Debug : w/ debug symbols, w/o optimization
|
||||
# Release : w/o debug symbols, w/ optimization
|
||||
# RelWithDebInfo : w/ debug symbols, w/ optimization
|
||||
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
|
||||
#set(ROS_BUILD_TYPE RelWithDebInfo)
|
||||
|
||||
rosbuild_init()
|
||||
|
||||
#set the default path for built executables to the "bin" directory
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
#set the default path for built libraries to the "lib" directory
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
|
||||
#uncomment if you have defined messages
|
||||
#rosbuild_genmsg()
|
||||
#uncomment if you have defined services
|
||||
#rosbuild_gensrv()
|
||||
|
||||
#common commands for building c++ executables and libraries
|
||||
#rosbuild_add_library(${PROJECT_NAME} src/example.cpp)
|
||||
#target_link_libraries(${PROJECT_NAME} another_library)
|
||||
#rosbuild_add_boost_directories()
|
||||
#rosbuild_link_boost(${PROJECT_NAME} thread)
|
||||
#rosbuild_add_executable(example examples/example.cpp)
|
||||
#target_link_libraries(example ${PROJECT_NAME})
|
|
@ -1 +0,0 @@
|
|||
include $(shell rospack find mk)/cmake.mk
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
\mainpage
|
||||
\htmlinclude manifest.html
|
||||
|
||||
\b rosgraph contains command-line and graphical tools for visualizing the ROS Computation Graph.
|
||||
|
||||
\section commandline Command-line tools
|
||||
|
||||
\subsection rxgraph rxgraph
|
||||
|
||||
\b rxgraph is a graphical tool that visualizes the connections between ROS nodes and topics.
|
||||
|
||||
\verbatim
|
||||
$ rxgraph
|
||||
\endverbatim
|
||||
|
||||
\subsection rosgraph rosgraph
|
||||
|
||||
The \b rosgraph command-line tool that displays information about connectivity between ROS nodes.
|
||||
|
||||
\subsubsection Usage
|
||||
|
||||
\verbatim
|
||||
$ rosgraph
|
||||
\endverbatim
|
||||
|
||||
|
||||
*/
|
|
@ -1,20 +0,0 @@
|
|||
<package>
|
||||
<description brief="rosgraph command-line tool">
|
||||
|
||||
<p>
|
||||
rosgraph contains the rosgraph command-line tool, which prints information about the ROS Computation Graph. It also provides an internal library that is used by the graphical version of this tool, <tt>rxgraph</tt>.
|
||||
</p>
|
||||
</description>
|
||||
<author>Ken Conley</author>
|
||||
<license>BSD</license>
|
||||
<review status="Doc reviewed" notes="2009/12/30"/>
|
||||
<url>http://ros.org/wiki/rosgraph</url>
|
||||
<depend package="roslib"/>
|
||||
|
||||
<platform os="ubuntu" version="9.04"/>
|
||||
<platform os="ubuntu" version="9.10"/>
|
||||
<platform os="ubuntu" version="10.04"/>
|
||||
<platform os="macports" version="macports"/>
|
||||
</package>
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2008, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id: rosgraph 3804 2009-02-11 02:16:00Z rob_wheeler $
|
||||
|
||||
import roslib; roslib.load_manifest('rosgraph')
|
||||
import rosgraph.rosgraph_main
|
||||
rosgraph.rosgraph_main.rosgraph_main()
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2008, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id: rxgraph 3804 2009-02-11 02:16:00Z rob_wheeler $
|
||||
|
||||
import roslib; roslib.load_manifest('rosgraph')
|
||||
import rosgraph.rxgraph
|
||||
rosgraph.rxgraph.rxgraph_main()
|
|
@ -1,34 +0,0 @@
|
|||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2009, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id$
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2009, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id: __init__.py 5735 2009-08-20 21:31:27Z sfkwc $
|
||||
|
|
@ -1,580 +0,0 @@
|
|||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2008, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id$
|
||||
|
||||
"""
|
||||
Data structures and library for representing ROS Computation Graph state.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import itertools
|
||||
import random
|
||||
import logging
|
||||
import traceback
|
||||
import xmlrpclib
|
||||
import socket
|
||||
|
||||
import roslib.scriptutil
|
||||
|
||||
logger = logging.getLogger('rosgraph.graph')
|
||||
|
||||
_ROS_NAME = '/rosviz'
|
||||
|
||||
def topic_node(topic):
|
||||
"""
|
||||
In order to prevent topic/node name aliasing, we have to remap
|
||||
topic node names. Currently we just prepend a space, which is
|
||||
an illegal ROS name and thus not aliased.
|
||||
@return str: topic mapped to a graph node name.
|
||||
"""
|
||||
return ' ' + topic
|
||||
def node_topic(node):
|
||||
"""
|
||||
Inverse of topic_node
|
||||
@return str: undo topic_node() operation
|
||||
"""
|
||||
return node[1:]
|
||||
|
||||
class BadNode(object):
|
||||
"""
|
||||
Data structure for storing info about a 'bad' node
|
||||
"""
|
||||
|
||||
## no connectivity
|
||||
DEAD = 0
|
||||
## intermittent connectivity
|
||||
WONKY = 1
|
||||
|
||||
def __init__(self, name, type, reason):
|
||||
"""
|
||||
@param type: DEAD | WONKY
|
||||
@type type: int
|
||||
"""
|
||||
self.name =name
|
||||
self.reason = reason
|
||||
self.type = type
|
||||
|
||||
class EdgeList(object):
|
||||
"""
|
||||
Data structure for storing Edge instances
|
||||
"""
|
||||
__slots__ = ['edges_by_start', 'edges_by_end']
|
||||
def __init__(self):
|
||||
# in order to make it easy to purge edges, we double-index them
|
||||
self.edges_by_start = {}
|
||||
self.edges_by_end = {}
|
||||
|
||||
def __iter__(self):
|
||||
return itertools.chain(*[v for v in self.edges_by_start.itervalues()])
|
||||
|
||||
def has(self, edge):
|
||||
"""
|
||||
@return: True if edge is in edge list
|
||||
@rtype: bool
|
||||
"""
|
||||
key = edge.key
|
||||
return key in self.edges_by_start and \
|
||||
edge in self.edges_by_start[key]
|
||||
|
||||
def add(self, edge):
|
||||
"""
|
||||
Add an edge to our internal representation. not multi-thread safe
|
||||
@param edge: edge to add
|
||||
@type edge: Edge
|
||||
"""
|
||||
# see note in __init__
|
||||
def update_map(map, key, edge):
|
||||
if key in map:
|
||||
l = map[key]
|
||||
if not edge in l:
|
||||
l.append(edge)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
map[key] = [edge]
|
||||
return True
|
||||
|
||||
updated = update_map(self.edges_by_start, edge.key, edge)
|
||||
updated = update_map(self.edges_by_end, edge.rkey, edge) or updated
|
||||
return updated
|
||||
|
||||
def add_edges(self, start, dest, direction, label=''):
|
||||
"""
|
||||
Create Edge instances for args and add resulting edges to edge
|
||||
list. Convenience method to avoid repetitve logging, etc...
|
||||
@param edge_list: data structure to add edge to
|
||||
@type edge_list: EdgeList
|
||||
@param start: name of start node. If None, warning will be logged and add fails
|
||||
@type start: str
|
||||
@param dest: name of start node. If None, warning will be logged and add fails
|
||||
@type dest: str
|
||||
@param direction: direction string (i/o/b)
|
||||
@type direction: str
|
||||
@return: True if update occured
|
||||
@rtype: bool
|
||||
"""
|
||||
|
||||
# the warnings should generally be temporary, occuring of the
|
||||
# master/node information becomes stale while we are still
|
||||
# doing an update
|
||||
updated = False
|
||||
if not start:
|
||||
logger.warn("cannot add edge: cannot map start [%s] to a node name", start)
|
||||
elif not dest:
|
||||
logger.warn("cannot add edge: cannot map dest [%s] to a node name", dest)
|
||||
else:
|
||||
for args in edge_args(start, dest, direction, label):
|
||||
updated = self.add(Edge(*args)) or updated
|
||||
return updated
|
||||
|
||||
def delete_all(self, node):
|
||||
"""
|
||||
Delete all edges that start or end at node
|
||||
@param node: name of node
|
||||
@type node: str
|
||||
"""
|
||||
def matching(map, pref):
|
||||
return [map[k] for k in map.iterkeys() if k.startswith(pref)]
|
||||
|
||||
pref = node+"|"
|
||||
edge_lists = matching(self.edges_by_start, pref) + matching(self.edges_by_start, pref)
|
||||
for el in edge_lists:
|
||||
for e in el:
|
||||
self.delete(e)
|
||||
|
||||
def delete(self, edge):
|
||||
# see note in __init__
|
||||
def update_map(map, key, edge):
|
||||
if key in map:
|
||||
edges = map[key]
|
||||
if edge in edges:
|
||||
edges.remove(edge)
|
||||
return True
|
||||
update_map(self.edges_by_start, edge.key, edge)
|
||||
update_map(self.edges_by_end, edge.rkey, edge)
|
||||
|
||||
class Edge(object):
|
||||
"""
|
||||
Data structure for representing ROS node graph edge
|
||||
"""
|
||||
|
||||
__slots__ = ['start', 'end', 'label', 'key', 'rkey']
|
||||
def __init__(self, start, end, label=''):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.label = label
|
||||
self.key = "%s|%s"%(self.start, self.label)
|
||||
# reverse key, indexed from end
|
||||
self.rkey = "%s|%s"%(self.end, self.label)
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.start != other.start or self.end != other.end
|
||||
def __str__(self):
|
||||
return "%s->%s"%(self.start, self.end)
|
||||
def __eq__(self, other):
|
||||
return self.start == other.start and self.end == other.end
|
||||
|
||||
def edge_args(start, dest, direction, label):
|
||||
"""
|
||||
compute argument ordering for Edge constructor based on direction flag
|
||||
@param direction str: 'i', 'o', or 'b' (in/out/bidir) relative to \a start
|
||||
@param start str: name of starting node
|
||||
@param start dest: name of destination node
|
||||
"""
|
||||
edge_args = []
|
||||
if direction in ['o', 'b']:
|
||||
edge_args.append((start, dest, label))
|
||||
if direction in ['i', 'b']:
|
||||
edge_args.append((dest, start, label))
|
||||
return edge_args
|
||||
|
||||
|
||||
class Graph(object):
|
||||
"""
|
||||
Utility class for polling ROS statistics from running ROS graph.
|
||||
Not multi-thread-safe
|
||||
"""
|
||||
|
||||
def __init__(self, node_ns='/', topic_ns='/'):
|
||||
self.master = roslib.scriptutil.get_master()
|
||||
|
||||
self.node_ns = node_ns or '/'
|
||||
self.topic_ns = topic_ns or '/'
|
||||
|
||||
# ROS nodes
|
||||
self.nn_nodes = set([])
|
||||
# ROS topic nodes
|
||||
self.nt_nodes = set([])
|
||||
|
||||
# ROS nodes that aren't responding quickly
|
||||
self.bad_nodes = {}
|
||||
import threading
|
||||
self.bad_nodes_lock = threading.Lock()
|
||||
|
||||
# ROS services
|
||||
self.srvs = set([])
|
||||
# ROS node->node transport connections
|
||||
self.nn_edges = EdgeList()
|
||||
# ROS node->topic connections
|
||||
self.nt_edges = EdgeList()
|
||||
# ROS all node->topic connections, including empty
|
||||
self.nt_all_edges = EdgeList()
|
||||
|
||||
# node names to URI map
|
||||
self.node_uri_map = {} # { node_name_str : uri_str }
|
||||
# reverse map URIs to node names
|
||||
self.uri_node_map = {} # { uri_str : node_name_str }
|
||||
|
||||
# time we last contacted master
|
||||
self.last_master_refresh = 0
|
||||
self.last_node_refresh = {}
|
||||
|
||||
# time we last communicated with master
|
||||
# seconds until master data is considered stale
|
||||
self.master_stale = 5.0
|
||||
# time we last communicated with node
|
||||
# seconds until node data is considered stale
|
||||
self.node_stale = 5.0 #seconds
|
||||
|
||||
|
||||
def set_master_stale(self, stale_secs):
|
||||
"""
|
||||
@param stale_secs: seconds that data is considered fresh
|
||||
@type stale_secs: double
|
||||
"""
|
||||
self.master_stale = stale_secs
|
||||
|
||||
def set_node_stale(self, stale_secs):
|
||||
"""
|
||||
@param stale_secs: seconds that data is considered fresh
|
||||
@type stale_secs: double
|
||||
"""
|
||||
self.node_stale = stale_secs
|
||||
|
||||
def _master_refresh(self):
|
||||
"""
|
||||
@return: True if nodes information was updated
|
||||
@rtype: bool
|
||||
"""
|
||||
logger.debug("master refresh: starting")
|
||||
updated = False
|
||||
#TODO: getSystemState probably needs to return URIs instead
|
||||
try:
|
||||
code, msg, val = self.master.getSystemState(_ROS_NAME)
|
||||
except socket.error, msg:
|
||||
print >> sys.stderr, "Unable to contact master", msg
|
||||
logger.error("unable to contact master: %s", msg)
|
||||
return False
|
||||
if code != 1:
|
||||
print >> sys.stderr, "Unable to contact master: %s"%msg
|
||||
logger.error("unable to contact master: %s", msg)
|
||||
updated = False
|
||||
else:
|
||||
pubs, subs, srvs = val
|
||||
|
||||
nodes = []
|
||||
nt_all_edges = self.nt_all_edges
|
||||
nt_nodes = self.nt_nodes
|
||||
for state, direction in ((pubs, 'o'), (subs, 'i')):
|
||||
for topic, l in state:
|
||||
if topic.startswith(self.topic_ns):
|
||||
nodes.extend([n for n in l if n.startswith(self.node_ns)])
|
||||
nt_nodes.add(topic_node(topic))
|
||||
for node in l:
|
||||
updated = nt_all_edges.add_edges(
|
||||
node, topic_node(topic), direction) or updated
|
||||
|
||||
nodes = set(nodes)
|
||||
|
||||
srvs = set([s for s, _ in srvs])
|
||||
purge = None
|
||||
if nodes ^ self.nn_nodes:
|
||||
purge = self.nn_nodes - nodes
|
||||
self.nn_nodes = nodes
|
||||
updated = True
|
||||
if srvs ^ self.srvs:
|
||||
self.srvs = srvs
|
||||
updated = True
|
||||
|
||||
if purge:
|
||||
logger.debug("following nodes and related edges will be purged: %s", ','.join(purge))
|
||||
for p in purge:
|
||||
logger.debug('purging edges for node %s', p)
|
||||
self.nn_edges.delete_all(p)
|
||||
self.nt_edges.delete_all(p)
|
||||
self.nt_all_edges.delete_all(p)
|
||||
|
||||
logger.debug("master refresh: done, updated[%s]", updated)
|
||||
return updated
|
||||
|
||||
def _mark_bad_node(self, node, reason):
|
||||
try:
|
||||
# bad nodes are updated in a separate thread, so lock
|
||||
self.bad_nodes_lock.acquire()
|
||||
if node in self.bad_nodes:
|
||||
self.bad_nodes[node].type = BadNode.DEAD
|
||||
else:
|
||||
self.bad_nodes[node] = (BadNode(node, BadNode.DEAD, reason))
|
||||
finally:
|
||||
self.bad_nodes_lock.release()
|
||||
|
||||
def _unmark_bad_node(self, node, reason):
|
||||
"""
|
||||
Promotes bad node to 'wonky' status.
|
||||
"""
|
||||
try:
|
||||
# bad nodes are updated in a separate thread, so lock
|
||||
self.bad_nodes_lock.acquire()
|
||||
bad = self.bad_nodes[node]
|
||||
bad.type = BadNode.WONKY
|
||||
finally:
|
||||
self.bad_nodes_lock.release()
|
||||
|
||||
def _node_refresh_businfo(self, node, api, bad_node=False):
|
||||
"""
|
||||
Retrieve bus info from the node and update nodes and edges as appropriate
|
||||
@param node: node name
|
||||
@type node: str
|
||||
@param api: XML-RPC proxy
|
||||
@type api: ServerProxy
|
||||
@param bad_node: If True, node has connectivity issues and
|
||||
should be treated differently
|
||||
@type bad_node: bool
|
||||
"""
|
||||
try:
|
||||
logger.debug("businfo: contacting node [%s] for bus info", node)
|
||||
|
||||
# unmark bad node, though it stays on the bad list
|
||||
if bad_node:
|
||||
self._unmark_bad_node(node)
|
||||
# Lower the socket timeout as we cannot abide by slow HTTP timeouts.
|
||||
# If a node cannot meet this timeout, it goes on the bad list
|
||||
# TODO: override transport instead.
|
||||
old_timeout = socket.getdefaulttimeout()
|
||||
if bad_node:
|
||||
#even stricter timeout for bad_nodes right now
|
||||
socket.setdefaulttimeout(0.2)
|
||||
else:
|
||||
socket.setdefaulttimeout(1.0)
|
||||
|
||||
code, msg, bus_info = api.getBusInfo(_ROS_NAME)
|
||||
|
||||
socket.setdefaulttimeout(old_timeout)
|
||||
except Exception, e:
|
||||
# node is (still) bad
|
||||
self._mark_bad_node(node, str(e))
|
||||
code = -1
|
||||
msg = traceback.format_exc()
|
||||
|
||||
updated = False
|
||||
if code != 1:
|
||||
logger.error("cannot get stats info from node [%s]: %s", node, msg)
|
||||
else:
|
||||
# [[connectionId1, destinationId1, direction1, transport1, ...]... ]
|
||||
for info in bus_info:
|
||||
connection_id = info[0]
|
||||
dest_id = info[1]
|
||||
direction = info[2]
|
||||
transport = info[3]
|
||||
topic = info[4]
|
||||
if len(info) > 5:
|
||||
connected = info[5]
|
||||
else:
|
||||
connected = True #backwards compatibility
|
||||
|
||||
if connected and topic.startswith(self.topic_ns):
|
||||
# blindly add as we will be able to catch state change via edges.
|
||||
# this currently means we don't cleanup topics
|
||||
self.nt_nodes.add(topic_node(topic))
|
||||
|
||||
# update node->topic->node graph edges
|
||||
updated = self.nt_edges.add_edges(node, topic_node(topic), direction) or updated
|
||||
|
||||
# update node->node graph edges
|
||||
if dest_id.startswith('http://'):
|
||||
#print "FOUND URI", dest_id
|
||||
dest_name = self.uri_node_map.get(dest_id, None)
|
||||
updated = self.nn_edges.add_edges(node, dest_name, direction, topic) or updated
|
||||
else:
|
||||
#TODO: anyting to do here?
|
||||
pass
|
||||
return updated
|
||||
|
||||
def _node_refresh(self, node, bad_node=False):
|
||||
"""
|
||||
Contact node for stats/connectivity information
|
||||
@param node: name of node to contact
|
||||
@type node: str
|
||||
@param bad_node: if True, node has connectivity issues
|
||||
@type bad_node: bool
|
||||
@return: True if node was successfully contacted
|
||||
@rtype bool
|
||||
"""
|
||||
# TODO: I'd like for master to provide this information in
|
||||
# getSystemState() instead to prevent the extra connection per node
|
||||
updated = False
|
||||
uri = self._node_uri_refresh(node)
|
||||
try:
|
||||
if uri:
|
||||
api = xmlrpclib.ServerProxy(uri)
|
||||
updated = self._node_refresh_businfo(node, api, bad_node)
|
||||
except KeyError, e:
|
||||
logger.warn('cannot contact node [%s] as it is not in the lookup table'%node)
|
||||
return updated
|
||||
|
||||
def _node_uri_refresh(self, node):
|
||||
try:
|
||||
code, msg, uri = self.master.lookupNode(_ROS_NAME, node)
|
||||
except:
|
||||
code = -1
|
||||
msg = traceback.format_exc()
|
||||
if code != 1:
|
||||
#TODO:REMOVE
|
||||
print >> sys.stderr, "Unknown node reported: %s"%msg
|
||||
logger.warn("master reported error in node lookup: %s"%msg)
|
||||
return None
|
||||
else:
|
||||
# update maps
|
||||
self.node_uri_map[node] = uri
|
||||
self.uri_node_map[uri] = node
|
||||
return uri
|
||||
|
||||
def _node_uri_refresh_all(self):
|
||||
"""
|
||||
Build self.node_uri_map and self.uri_node_map using master as a
|
||||
lookup service. This will make N requests to the master for N
|
||||
nodes, so this should only be used sparingly
|
||||
"""
|
||||
for node in self.nn_nodes:
|
||||
self._node_uri_refresh(node)
|
||||
|
||||
def bad_update(self):
|
||||
"""
|
||||
Update loop for nodes with bad connectivity. We box them separately
|
||||
so that we can maintain the good performance of the normal update loop.
|
||||
Once a node is on the bad list it stays there.
|
||||
"""
|
||||
last_node_refresh = self.last_node_refresh
|
||||
|
||||
# nodes left to check
|
||||
try:
|
||||
self.bad_nodes_lock.acquire()
|
||||
# make copy due to multithreading
|
||||
update_queue = self.bad_nodes.values()[:]
|
||||
finally:
|
||||
self.bad_nodes_lock.release()
|
||||
|
||||
# return value. True if new data differs from old
|
||||
updated = False
|
||||
# number of nodes we checked
|
||||
num_nodes = 0
|
||||
|
||||
start_time = time.time()
|
||||
while update_queue:
|
||||
# figure out the next node to contact
|
||||
next = update_queue.pop()
|
||||
# rate limit talking to any particular node
|
||||
if time.time() > (last_node_refresh.get(next, 0.0) + self.node_stale):
|
||||
updated = self._node_refresh(next.name, True) or updated
|
||||
# include in random offset (max 1/5th normal update interval)
|
||||
# to help spread out updates
|
||||
last_node_refresh[next] = time.time() + (random.random() * self.node_stale / 5.0)
|
||||
num_nodes += 1
|
||||
|
||||
# small yield to keep from torquing the processor
|
||||
time.sleep(0.01)
|
||||
end_time = time.time()
|
||||
#print "Update (bad nodes) took %ss for %s nodes"%((end_time-start_time), num_nodes)
|
||||
logger.debug("ROS stats (bad nodes) update took %ss"%(end_time-start_time))
|
||||
return updated
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Update all the stats. This method may take awhile to complete as it will
|
||||
communicate with all nodes + master.
|
||||
"""
|
||||
|
||||
last_node_refresh = self.last_node_refresh
|
||||
|
||||
# nodes left to check
|
||||
update_queue = None
|
||||
# True if there are still more stats to fetch this cycle
|
||||
work_to_do = True
|
||||
# return value. True if new data differs from old
|
||||
updated = False
|
||||
# number of nodes we checked
|
||||
num_nodes = 0
|
||||
|
||||
start_time = time.time()
|
||||
while work_to_do:
|
||||
|
||||
# each time through the loop try to talk to either the master
|
||||
# or a node. stop when we have talked to everybody.
|
||||
|
||||
# get a new node list from the master
|
||||
if time.time() > (self.last_master_refresh + self.master_stale):
|
||||
updated = self._master_refresh()
|
||||
if self.last_master_refresh == 0:
|
||||
# first time we contact the master, also refresh our full lookup tables
|
||||
self._node_uri_refresh_all()
|
||||
|
||||
self.last_master_refresh = time.time()
|
||||
# contact the nodes for stats
|
||||
else:
|
||||
# initialize update_queue based on most current nodes list
|
||||
if update_queue is None:
|
||||
update_queue = list(self.nn_nodes)
|
||||
# no nodes left to contact
|
||||
elif not update_queue:
|
||||
work_to_do = False
|
||||
# contact next node
|
||||
else:
|
||||
# figure out the next node to contact
|
||||
next = update_queue.pop()
|
||||
# rate limit talking to any particular node
|
||||
if time.time() > (last_node_refresh.get(next, 0.0) + self.node_stale):
|
||||
updated = self._node_refresh(next) or updated
|
||||
# include in random offset (max 1/5th normal update interval)
|
||||
# to help spread out updates
|
||||
last_node_refresh[next] = time.time() + (random.random() * self.node_stale / 5.0)
|
||||
num_nodes += 1
|
||||
|
||||
# small yield to keep from torquing the processor
|
||||
time.sleep(0.01)
|
||||
end_time = time.time()
|
||||
#print "Update took %ss for %s nodes"%((end_time-start_time), num_nodes)
|
||||
logger.debug("ROS stats update took %ss"%(end_time-start_time))
|
||||
return updated
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2009, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id: masterapi.py 9672 2010-05-11 21:57:40Z kwc $
|
||||
"""
|
||||
Python adapter for calling ROS Master API. While it is trivial to call the
|
||||
Master directly using XML-RPC, this API provides a safer abstraction in the event
|
||||
the Master API is changed.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import xmlrpclib
|
||||
|
||||
import roslib.exceptions
|
||||
import roslib.names
|
||||
|
||||
class ROSMasterException(roslib.exceptions.ROSLibException):
|
||||
"""
|
||||
Base class of ROS-master related errors.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Failure(ROSMasterException):
|
||||
"""
|
||||
Call to Master failed. This generally indicates an internal error
|
||||
in the Master and that the Master may be in an inconsistent state.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Error(ROSMasterException):
|
||||
"""
|
||||
Master returned an error code, which indicates an error in the
|
||||
arguments passed to the Master.
|
||||
"""
|
||||
pass
|
||||
|
||||
def is_online(master_uri=None):
|
||||
"""
|
||||
@param master_uri: (optional) override environment's ROS_MASTER_URI
|
||||
@type master_uri: str
|
||||
@return: True if Master is available
|
||||
"""
|
||||
return Master('roslib', master_uri=master_uri).is_online()
|
||||
|
||||
class Master(object):
|
||||
"""
|
||||
API for interacting with the ROS master. Although the Master is
|
||||
relatively simple to interact with using the XMLRPC API, this
|
||||
abstraction layer provides protection against future updates. It
|
||||
also provides a streamlined API with builtin return code checking
|
||||
and caller_id passing.
|
||||
"""
|
||||
|
||||
def __init__(self, caller_id, master_uri=None):
|
||||
"""
|
||||
@param caller_id: name of node to use in calls to master
|
||||
@type caller_id: str
|
||||
@param master_uri: (optional) override environment's ROS_MASTER_URI
|
||||
@type master_uri: str
|
||||
"""
|
||||
|
||||
if master_uri is None:
|
||||
master_uri = roslib.rosenv.get_master_uri()
|
||||
self._reinit(master_uri)
|
||||
|
||||
self.caller_id = roslib.names.make_caller_id(caller_id) #resolve
|
||||
if self.caller_id[-1] == '/':
|
||||
self.caller_id = self.caller_id[:-1]
|
||||
|
||||
def _reinit(self, master_uri):
|
||||
"""
|
||||
Internal API for reinitializing this handle to be a new master
|
||||
"""
|
||||
self.master_uri = master_uri
|
||||
self.handle = xmlrpclib.ServerProxy(self.master_uri)
|
||||
|
||||
def is_online(self):
|
||||
"""
|
||||
Check if Master is online.
|
||||
|
||||
NOTE: this is not part of the actual Master API. This is a convenience function.
|
||||
|
||||
@param master_uri: (optional) override environment's ROS_MASTER_URI
|
||||
@type master_uri: str
|
||||
@return: True if Master is available
|
||||
"""
|
||||
try:
|
||||
self.getPid()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def _succeed(self, args):
|
||||
"""
|
||||
Check master return code and return the value field.
|
||||
|
||||
@param args: master return value
|
||||
@type args: (int, str, XMLRPCLegalValue)
|
||||
@return: value field of args (master return value)
|
||||
@rtype: XMLRPCLegalValue
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
code, msg, val = args
|
||||
if code == 1:
|
||||
return val
|
||||
elif code == -1:
|
||||
raise Error(msg)
|
||||
else:
|
||||
raise Failure(msg)
|
||||
|
||||
################################################################################
|
||||
# PARAM SERVER
|
||||
|
||||
def deleteParam(self, key):
|
||||
"""
|
||||
Parameter Server: delete parameter
|
||||
@param key: parameter name
|
||||
@type key: str
|
||||
@return: 0
|
||||
@rtype: int
|
||||
"""
|
||||
return self._succeed(self.handle.deleteParam(self.caller_id, key))
|
||||
|
||||
def setParam(self, key, value):
|
||||
"""
|
||||
Parameter Server: set parameter. NOTE: if value is a
|
||||
dictionary it will be treated as a parameter tree, where key
|
||||
is the parameter namespace. For example:::
|
||||
{'x':1,'y':2,'sub':{'z':3}}
|
||||
|
||||
will set key/x=1, key/y=2, and key/sub/z=3. Furthermore, it
|
||||
will replace all existing parameters in the key parameter
|
||||
namespace with the parameters in value. You must set
|
||||
parameters individually if you wish to perform a union update.
|
||||
|
||||
@param key: parameter name
|
||||
@type key: str
|
||||
@param value: parameter value.
|
||||
@type value: XMLRPCLegalValue
|
||||
@return: 0
|
||||
@rtype: int
|
||||
"""
|
||||
return self._succeed(self.handle.setParam(self.caller_id, key, value))
|
||||
|
||||
def getParam(self, key):
|
||||
"""
|
||||
Retrieve parameter value from server.
|
||||
@param key: parameter to lookup. If key is a namespace,
|
||||
getParam() will return a parameter tree.
|
||||
@type key: str
|
||||
getParam() will return a parameter tree.
|
||||
|
||||
@return: parameterValue. If key is a namespace,
|
||||
the return value will be a dictionary, where each key is a
|
||||
parameter in that namespace. Sub-namespaces are also
|
||||
represented as dictionaries.
|
||||
@rtype: XMLRPCLegalValue
|
||||
"""
|
||||
return self._succeed(self.handle.getParam(self.caller_id, key))
|
||||
|
||||
def searchParam(self, key):
|
||||
"""
|
||||
Search for parameter key on parameter server. Search starts in caller's namespace and proceeds
|
||||
upwards through parent namespaces until Parameter Server finds a matching key.
|
||||
|
||||
searchParam's behavior is to search for the first partial match.
|
||||
For example, imagine that there are two 'robot_description' parameters::
|
||||
|
||||
/robot_description
|
||||
/robot_description/arm
|
||||
/robot_description/base
|
||||
/pr2/robot_description
|
||||
/pr2/robot_description/base
|
||||
|
||||
If I start in the namespace /pr2/foo and search for
|
||||
'robot_description', searchParam will match
|
||||
/pr2/robot_description. If I search for 'robot_description/arm'
|
||||
it will return /pr2/robot_description/arm, even though that
|
||||
parameter does not exist (yet).
|
||||
|
||||
@param key: parameter key to search for.
|
||||
@type key: str
|
||||
@return: foundKey
|
||||
@rtype: str
|
||||
"""
|
||||
return self._succeed(self.handle.searchParam(self.caller_id, key))
|
||||
|
||||
def subscribeParam(self, caller_api, key):
|
||||
"""
|
||||
Retrieve parameter value from server and subscribe to updates to that param. See
|
||||
paramUpdate() in the Node API.
|
||||
@param key: parameter to lookup.
|
||||
@type key: str
|
||||
@param caller_api: API URI for paramUpdate callbacks.
|
||||
@type caller_api: str
|
||||
@return: parameterValue. parameterValue is an empty dictionary if the parameter has not been set yet.
|
||||
@rtype: XMLRPCLegalValue
|
||||
"""
|
||||
return self._succeed(self.handle.subscribeParam(self.caller_id, caller_api, key))
|
||||
|
||||
def unsubscribeParam(self, caller_api, key):
|
||||
"""
|
||||
Retrieve parameter value from server and subscribe to updates to that param. See
|
||||
paramUpdate() in the Node API.
|
||||
@param key: parameter to lookup.
|
||||
@type key: str
|
||||
@param caller_api: API URI for paramUpdate callbacks.
|
||||
@type caller_api: str
|
||||
@return: numUnsubscribed. If numUnsubscribed is zero it means that the caller was not subscribed to the parameter.
|
||||
@rtype: int
|
||||
"""
|
||||
return self._succeed(self.handle.unsubscribeParam(self.caller_id, caller_api, key))
|
||||
|
||||
def hasParam(self, key):
|
||||
"""
|
||||
Check if parameter is stored on server.
|
||||
@param key: parameter to check
|
||||
@type key: str
|
||||
@return: [code, statusMessage, hasParam]
|
||||
@rtype: [int, str, bool]
|
||||
"""
|
||||
return self._succeed(self.handle.hasParam(self.caller_id, key))
|
||||
|
||||
def getParamNames(self):
|
||||
"""
|
||||
Get list of all parameter names stored on this server.
|
||||
This does not adjust parameter names for caller's scope.
|
||||
|
||||
@return: [code, statusMessage, parameterNameList]
|
||||
@rtype: [int, str, [str]]
|
||||
"""
|
||||
return self._succeed(self.handle.getParamNames(self.caller_id))
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
def getPid(self):
|
||||
"""
|
||||
Get the PID of this server
|
||||
@return: serverProcessPID
|
||||
@rtype: int
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.getPid(self.caller_id))
|
||||
|
||||
def getUri(self):
|
||||
"""
|
||||
Get the URI of this Master
|
||||
@return: masterUri
|
||||
@rtype: str
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.getUri(self.caller_id))
|
||||
|
||||
def registerService(self, service, service_api, caller_api):
|
||||
"""
|
||||
Register the caller as a provider of the specified service.
|
||||
@param service str: Fully-qualified name of service
|
||||
@param service_api str: Service URI
|
||||
@param caller_api str: XML-RPC URI of caller node
|
||||
@return: ignore
|
||||
@rtype: int
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.registerService(self.caller_id, service, service_api, caller_api))
|
||||
|
||||
def lookupService(self, service):
|
||||
"""
|
||||
Lookup all provider of a particular service.
|
||||
@param service: fully-qualified name of service to lookup.
|
||||
@type: service: str
|
||||
@return (int, str, str): (code, message, serviceUrl). service URL is provides
|
||||
and address and port of the service. Fails if there is no provider.
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.lookupService(self.caller_id, service))
|
||||
|
||||
|
||||
def unregisterService(self, service, service_api):
|
||||
"""
|
||||
Unregister the caller as a provider of the specified service.
|
||||
@param service: Fully-qualified name of service
|
||||
@type service: str
|
||||
@param service_api: API URI of service to unregister. Unregistration will only occur if current
|
||||
registration matches.
|
||||
@type service_api: str
|
||||
@return: (code, message, numUnregistered). Number of unregistrations (either 0 or 1).
|
||||
If this is zero it means that the caller was not registered as a service provider.
|
||||
The call still succeeds as the intended final state is reached.
|
||||
@rtype: (int, str, int)
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.unregisterService(self.caller_id, service, service_api))
|
||||
|
||||
|
||||
def registerSubscriber(self, topic, topic_type, caller_api):
|
||||
"""
|
||||
Subscribe the caller to the specified topic. In addition to receiving
|
||||
a list of current publishers, the subscriber will also receive notifications
|
||||
of new publishers via the publisherUpdate API.
|
||||
@param topic str: Fully-qualified name of topic to subscribe to.
|
||||
@param topic_type: Datatype for topic. Must be a package-resource name, i.e. the .msg name.
|
||||
@type topic_type: str
|
||||
@param caller_api: XML-RPC URI of caller node for new publisher notifications
|
||||
@type caller_api: str
|
||||
@return: (code, message, publishers). Publishers is a list of XMLRPC API URIs
|
||||
for nodes currently publishing the specified topic.
|
||||
@rtype: (int, str, list(str))
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.registerSubscriber(self.caller_id, topic, topic_type, caller_api))
|
||||
|
||||
|
||||
def unregisterSubscriber(self, topic, caller_api):
|
||||
"""
|
||||
Unregister the caller as a publisher of the topic.
|
||||
@param topic: Fully-qualified name of topic to unregister.
|
||||
@type topic: str
|
||||
@param caller_api: API URI of service to unregister. Unregistration will only occur if current
|
||||
@type caller_api: str
|
||||
registration matches.
|
||||
@return: (code, statusMessage, numUnsubscribed).
|
||||
If numUnsubscribed is zero it means that the caller was not registered as a subscriber.
|
||||
The call still succeeds as the intended final state is reached.
|
||||
@rtype: (int, str, int)
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.unregisterSubscriber(self.caller_id, topic, caller_api))
|
||||
|
||||
def registerPublisher(self, topic, topic_type, caller_api):
|
||||
"""
|
||||
Register the caller as a publisher the topic.
|
||||
@param topic: Fully-qualified name of topic to register.
|
||||
@type topic: str
|
||||
@param topic_type: Datatype for topic. Must be a
|
||||
package-resource name, i.e. the .msg name.
|
||||
@type topic_type: str
|
||||
@param caller_api str: ROS caller XML-RPC API URI
|
||||
@type caller_api: str
|
||||
@return: subscriberApis.
|
||||
List of current subscribers of topic in the form of XMLRPC URIs.
|
||||
@rtype: [str]
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.registerPublisher(self.caller_id, topic, topic_type, caller_api))
|
||||
|
||||
def unregisterPublisher(self, topic, caller_api):
|
||||
"""
|
||||
Unregister the caller as a publisher of the topic.
|
||||
@param topic: Fully-qualified name of topic to unregister.
|
||||
@type topic: str
|
||||
@param caller_api str: API URI of service to
|
||||
unregister. Unregistration will only occur if current
|
||||
registration matches.
|
||||
@type caller_api: str
|
||||
@return: numUnregistered.
|
||||
If numUnregistered is zero it means that the caller was not registered as a publisher.
|
||||
The call still succeeds as the intended final state is reached.
|
||||
@rtype: int
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.unregisterPublisher(self.caller_id, topic, caller_api))
|
||||
|
||||
def lookupNode(self, node_name):
|
||||
"""
|
||||
Get the XML-RPC URI of the node with the associated
|
||||
name/caller_id. This API is for looking information about
|
||||
publishers and subscribers. Use lookupService instead to lookup
|
||||
ROS-RPC URIs.
|
||||
@param node: name of node to lookup
|
||||
@type node: str
|
||||
@return: URI
|
||||
@rtype: str
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.lookupNode(self.caller_id, node_name))
|
||||
|
||||
def getPublishedTopics(self, subgraph):
|
||||
"""
|
||||
Get list of topics that can be subscribed to. This does not return topics that have no publishers.
|
||||
See L{getSystemState()} to get more comprehensive list.
|
||||
@param subgraph: Restrict topic names to match within the specified subgraph. Subgraph namespace
|
||||
is resolved relative to the caller's namespace. Use '' to specify all names.
|
||||
@type subgraph: str
|
||||
@return: [[topic1, type1]...[topicN, typeN]]
|
||||
@rtype: [[str, str],]
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.getPublishedTopics(self.caller_id, subgraph))
|
||||
|
||||
def getTopicTypes(self):
|
||||
"""
|
||||
Retrieve list topic names and their types.
|
||||
|
||||
New in ROS 1.2.
|
||||
|
||||
@param caller_id: ROS caller id
|
||||
@type caller_id: str
|
||||
@rtype: (int, str, [[str,str]] )
|
||||
@return: (code, statusMessage, topicTypes). topicTypes is a list of [topicName, topicType] pairs.
|
||||
"""
|
||||
return self._succeed(self.handle.getTopicTypes(self.caller_id))
|
||||
|
||||
def getSystemState(self):
|
||||
"""
|
||||
Retrieve list representation of system state (i.e. publishers, subscribers, and services).
|
||||
@rtype: [[str,[str]], [str,[str]], [str,[str]]]
|
||||
@return: systemState
|
||||
|
||||
System state is in list representation::
|
||||
[publishers, subscribers, services].
|
||||
|
||||
publishers is of the form::
|
||||
[ [topic1, [topic1Publisher1...topic1PublisherN]] ... ]
|
||||
|
||||
subscribers is of the form::
|
||||
[ [topic1, [topic1Subscriber1...topic1SubscriberN]] ... ]
|
||||
|
||||
services is of the form::
|
||||
[ [service1, [service1Provider1...service1ProviderN]] ... ]
|
||||
|
||||
@raise rosgraph.masterapi.Error: if Master returns ERROR.
|
||||
@raise rosgraph.masterapi.Failure: if Master returns FAILURE.
|
||||
"""
|
||||
return self._succeed(self.handle.getSystemState(self.caller_id))
|
|
@ -1,99 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2008, Willow Garage, Inc.
|
||||
# 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 Willow Garage, Inc. 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.
|
||||
#
|
||||
# Revision $Id$
|
||||
|
||||
import sys
|
||||
import time
|
||||
import roslib.roslogging
|
||||
import roslib.scriptutil
|
||||
import rosgraph.impl.graph
|
||||
import rosgraph.masterapi
|
||||
|
||||
def fullusage():
|
||||
print """rosgraph is a command-line tool for debugging the ROS Computation Graph.
|
||||
|
||||
Usage:
|
||||
\trosgraph
|
||||
"""
|
||||
|
||||
def rosgraph_main():
|
||||
if len(sys.argv) == 1:
|
||||
pass
|
||||
elif len(sys.argv) == 2 and sys.argv[1] == '-h':
|
||||
fullusage()
|
||||
return
|
||||
else:
|
||||
fullusage()
|
||||
sys.exit(-1)
|
||||
|
||||
roslib.roslogging.configure_logging('rosgraph')
|
||||
|
||||
# make sure master is available
|
||||
master = rosgraph.masterapi.Master('rosgraph')
|
||||
try:
|
||||
master.getPid()
|
||||
except:
|
||||
print >> sys.stderr, "ERROR: Unable to communicate with master!"
|
||||
return
|
||||
|
||||
g = rosgraph.impl.graph.Graph()
|
||||
try:
|
||||
while 1:
|
||||
g.update()
|
||||
|
||||
if not g.nn_nodes and not g.srvs:
|
||||
print "empty"
|
||||
else:
|
||||
print '\n'
|
||||
if g.nn_nodes:
|
||||
print 'Nodes:'
|
||||
for n in g.nn_nodes:
|
||||
print ' ' + n + ' :'
|
||||
print ' Inbound:'
|
||||
if n in g.nn_edges.edges_by_end:
|
||||
for c in g.nt_all_edges.edges_by_end[n]:
|
||||
print ' ' + c.start
|
||||
print ' Outbound:'
|
||||
if n in g.nn_edges.edges_by_start:
|
||||
for c in g.nt_all_edges.edges_by_start[n]:
|
||||
print ' ' + c.end
|
||||
if g.srvs:
|
||||
print 'Services:'
|
||||
for s in g.srvs:
|
||||
print ' ' + s
|
||||
|
||||
time.sleep(1.0)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
Loading…
Reference in New Issue