v01
This commit is contained in:
182
thirdparty/ros/ros_comm/tools/rosgraph/CHANGELOG.rst
vendored
Normal file
182
thirdparty/ros/ros_comm/tools/rosgraph/CHANGELOG.rst
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package rosgraph
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
1.12.14 (2018-08-23)
|
||||
--------------------
|
||||
|
||||
1.12.13 (2018-02-21)
|
||||
--------------------
|
||||
|
||||
1.12.12 (2017-11-16)
|
||||
--------------------
|
||||
|
||||
1.12.11 (2017-11-07)
|
||||
--------------------
|
||||
|
||||
1.12.10 (2017-11-06)
|
||||
--------------------
|
||||
|
||||
1.12.9 (2017-11-06)
|
||||
-------------------
|
||||
|
||||
1.12.8 (2017-11-06)
|
||||
-------------------
|
||||
* improve message when `roslogging` cannot change permissions (`#1068 <https://github.com/ros/ros_comm/issues/1068>`_)
|
||||
|
||||
1.12.7 (2017-02-17)
|
||||
-------------------
|
||||
|
||||
1.12.6 (2016-10-26)
|
||||
-------------------
|
||||
* change rospy default rosconsole format for consistency with roscpp (`#879 <https://github.com/ros/ros_comm/pull/879>`_)
|
||||
* increase request_queue_size for xmlrpc server (`#849 <https://github.com/ros/ros_comm/issues/849>`_)
|
||||
|
||||
1.12.5 (2016-09-30)
|
||||
-------------------
|
||||
|
||||
1.12.4 (2016-09-19)
|
||||
-------------------
|
||||
|
||||
1.12.3 (2016-09-17)
|
||||
-------------------
|
||||
* add 'Darwin' to unix-like platforms improving address resolution (`#846 <https://github.com/ros/ros_comm/pull/846>`_)
|
||||
* use logging Formatter, enabling printing exception info with exc_info=1 (`#828 <https://github.com/ros/ros_comm/pull/828>`_)
|
||||
* add `__contains_\_`, which is a better spelling of `has` (`#754 <https://github.com/ros/ros_comm/pull/754>`_)
|
||||
|
||||
1.12.2 (2016-06-03)
|
||||
-------------------
|
||||
* avoid creating a latest symlink for the root of the log dir (`#795 <https://github.com/ros/ros_comm/pull/795>`_)
|
||||
|
||||
1.12.1 (2016-04-18)
|
||||
-------------------
|
||||
* fix str conversion in encode_ros_handshake_header (`#792 <https://github.com/ros/ros_comm/pull/792>`_)
|
||||
|
||||
1.12.0 (2016-03-18)
|
||||
-------------------
|
||||
|
||||
1.11.18 (2016-03-17)
|
||||
--------------------
|
||||
|
||||
1.11.17 (2016-03-11)
|
||||
--------------------
|
||||
* fix raising classes not derived from `Exception` which caused a TypeError (`#761 <https://github.com/ros/ros_comm/pull/761>`_)
|
||||
* fix handshake header with Python 3 (`#759 <https://github.com/ros/ros_comm/issues/759>`_)
|
||||
* fix encoding of header fields (`#704 <https://github.com/ros/ros_comm/issues/704>`_)
|
||||
|
||||
1.11.16 (2015-11-09)
|
||||
--------------------
|
||||
|
||||
1.11.15 (2015-10-13)
|
||||
--------------------
|
||||
|
||||
1.11.14 (2015-09-19)
|
||||
--------------------
|
||||
* create a symlink to the latest log directory (`#659 <https://github.com/ros/ros_comm/pull/659>`_)
|
||||
|
||||
1.11.13 (2015-04-28)
|
||||
--------------------
|
||||
|
||||
1.11.12 (2015-04-27)
|
||||
--------------------
|
||||
|
||||
1.11.11 (2015-04-16)
|
||||
--------------------
|
||||
|
||||
1.11.10 (2014-12-22)
|
||||
--------------------
|
||||
* rosconsole format for rospy (`#517 <https://github.com/ros/ros_comm/issues/517>`_)
|
||||
* fix exception at roscore startup if python has IPv6 disabled (`#515 <https://github.com/ros/ros_comm/issues/515>`_)
|
||||
|
||||
1.11.9 (2014-08-18)
|
||||
-------------------
|
||||
|
||||
1.11.8 (2014-08-04)
|
||||
-------------------
|
||||
|
||||
1.11.7 (2014-07-18)
|
||||
-------------------
|
||||
|
||||
1.11.6 (2014-07-10)
|
||||
-------------------
|
||||
|
||||
1.11.5 (2014-06-24)
|
||||
-------------------
|
||||
|
||||
1.11.4 (2014-06-16)
|
||||
-------------------
|
||||
* Python 3 compatibility (`#426 <https://github.com/ros/ros_comm/issues/426>`_, `#427 <https://github.com/ros/ros_comm/issues/427>`_, `#429 <https://github.com/ros/ros_comm/issues/429>`_)
|
||||
|
||||
1.11.3 (2014-05-21)
|
||||
-------------------
|
||||
|
||||
1.11.2 (2014-05-08)
|
||||
-------------------
|
||||
|
||||
1.11.1 (2014-05-07)
|
||||
-------------------
|
||||
* add architecture_independent flag in package.xml (`#391 <https://github.com/ros/ros_comm/issues/391>`_)
|
||||
|
||||
1.11.0 (2014-03-04)
|
||||
-------------------
|
||||
* use catkin_install_python() to install Python scripts (`#361 <https://github.com/ros/ros_comm/issues/361>`_)
|
||||
|
||||
1.10.0 (2014-02-11)
|
||||
-------------------
|
||||
|
||||
1.9.54 (2014-01-27)
|
||||
-------------------
|
||||
|
||||
1.9.53 (2014-01-14)
|
||||
-------------------
|
||||
|
||||
1.9.52 (2014-01-08)
|
||||
-------------------
|
||||
|
||||
1.9.51 (2014-01-07)
|
||||
-------------------
|
||||
* allow different 127. addresses than 127.0.0.1 (`#315 <https://github.com/ros/ros_comm/issues/315>`_)
|
||||
* work around for nose 1.3.0 bug (`#323 <https://github.com/ros/ros_comm/issues/323>`_)
|
||||
|
||||
1.9.50 (2013-10-04)
|
||||
-------------------
|
||||
|
||||
1.9.49 (2013-09-16)
|
||||
-------------------
|
||||
|
||||
1.9.48 (2013-08-21)
|
||||
-------------------
|
||||
|
||||
1.9.47 (2013-07-03)
|
||||
-------------------
|
||||
* check for CATKIN_ENABLE_TESTING to enable configure without tests
|
||||
|
||||
1.9.46 (2013-06-18)
|
||||
-------------------
|
||||
|
||||
1.9.45 (2013-06-06)
|
||||
-------------------
|
||||
* add warnings for obviously wrong environment variables ROS_HOSTNAME and ROS_IP (`#134 <https://github.com/ros/ros_comm/issues/134>`_)
|
||||
* fix exception on netifaces.ifaddresses() (`#211 <https://github.com/ros/ros_comm/issues/211>`_, `#213 <https://github.com/ros/ros_comm/issues/213>`_) (regression from 1.9.42)
|
||||
|
||||
1.9.44 (2013-03-21)
|
||||
-------------------
|
||||
|
||||
1.9.43 (2013-03-13)
|
||||
-------------------
|
||||
|
||||
1.9.42 (2013-03-08)
|
||||
-------------------
|
||||
* replace custom code with Python module netifaces (`#130 <https://github.com/ros/ros_comm/issues/130>`_)
|
||||
* make dependencies on rospy optional by refactoring RosStreamHandler to rosgraph (`#179 <https://github.com/ros/ros_comm/issues/179>`_)
|
||||
|
||||
1.9.41 (2013-01-24)
|
||||
-------------------
|
||||
|
||||
1.9.40 (2013-01-13)
|
||||
-------------------
|
||||
* add colorization for rospy log output (`#3691 <https://code.ros.org/trac/ros/ticket/3691>`_)
|
||||
|
||||
1.9.39 (2012-12-29)
|
||||
-------------------
|
||||
* first public release for Groovy
|
||||
20
thirdparty/ros/ros_comm/tools/rosgraph/CMakeLists.txt
vendored
Normal file
20
thirdparty/ros/ros_comm/tools/rosgraph/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 2.8.3)
|
||||
project(rosgraph)
|
||||
find_package(catkin REQUIRED)
|
||||
catkin_package()
|
||||
|
||||
catkin_python_setup()
|
||||
|
||||
# logging config file goes in both etc and package layout (for now).
|
||||
# want to get rid of package layout copy, but need to be able to
|
||||
# locate etc first.
|
||||
install(FILES conf/python_logging.conf
|
||||
DESTINATION ${CATKIN_GLOBAL_ETC_DESTINATION}/ros)
|
||||
install(FILES conf/python_logging.conf
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/conf)
|
||||
catkin_install_python(PROGRAMS scripts/rosgraph
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
|
||||
|
||||
if(CATKIN_ENABLE_TESTING)
|
||||
catkin_add_nosetests(test)
|
||||
endif()
|
||||
35
thirdparty/ros/ros_comm/tools/rosgraph/conf/python_logging.conf
vendored
Normal file
35
thirdparty/ros/ros_comm/tools/rosgraph/conf/python_logging.conf
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
[loggers]
|
||||
keys=root, rosout
|
||||
|
||||
[handlers]
|
||||
keys=fileHandler,streamHandler
|
||||
|
||||
[formatters]
|
||||
keys=defaultFormatter
|
||||
|
||||
[logger_root]
|
||||
level=INFO
|
||||
handlers=fileHandler
|
||||
|
||||
[logger_rosout]
|
||||
level=INFO
|
||||
handlers=streamHandler
|
||||
propagate=1
|
||||
qualname=rosout
|
||||
|
||||
[handler_fileHandler]
|
||||
class=handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=defaultFormatter
|
||||
# log filename, mode, maxBytes, backupCount
|
||||
args=(os.environ['ROS_LOG_FILENAME'],'a', 50000000, 4)
|
||||
|
||||
[handler_streamHandler]
|
||||
class=rosgraph.roslogging.RosStreamHandler
|
||||
level=DEBUG
|
||||
formatter=defaultFormatter
|
||||
# colorize output flag
|
||||
args=(True,)
|
||||
|
||||
[formatter_defaultFormatter]
|
||||
format=[%(name)s][%(levelname)s] %(asctime)s: %(message)s
|
||||
26
thirdparty/ros/ros_comm/tools/rosgraph/package.xml
vendored
Normal file
26
thirdparty/ros/ros_comm/tools/rosgraph/package.xml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<package>
|
||||
<name>rosgraph</name>
|
||||
<version>1.12.14</version>
|
||||
<description>
|
||||
rosgraph contains the rosgraph command-line tool, which prints
|
||||
information about the ROS Computation Graph. It also provides an
|
||||
internal library that can be used by graphical tools.
|
||||
</description>
|
||||
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
|
||||
<license>BSD</license>
|
||||
|
||||
<url>http://ros.org/wiki/rosgraph</url>
|
||||
<author>Ken Conley</author>
|
||||
|
||||
<buildtool_depend version_gte="0.5.78">catkin</buildtool_depend>
|
||||
|
||||
<run_depend>python-netifaces</run_depend>
|
||||
<run_depend>python-rospkg</run_depend>
|
||||
|
||||
<test_depend>python-mock</test_depend>
|
||||
|
||||
<export>
|
||||
<rosdoc config="rosdoc.yaml"/>
|
||||
<architecture_independent/>
|
||||
</export>
|
||||
</package>
|
||||
1
thirdparty/ros/ros_comm/tools/rosgraph/rosdoc.yaml
vendored
Normal file
1
thirdparty/ros/ros_comm/tools/rosgraph/rosdoc.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
- builder: epydoc
|
||||
38
thirdparty/ros/ros_comm/tools/rosgraph/scripts/rosgraph
vendored
Executable file
38
thirdparty/ros/ros_comm/tools/rosgraph/scripts/rosgraph
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/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 rosgraph.rosgraph_main
|
||||
rosgraph.rosgraph_main.rosgraph_main()
|
||||
|
||||
13
thirdparty/ros/ros_comm/tools/rosgraph/setup.py
vendored
Normal file
13
thirdparty/ros/ros_comm/tools/rosgraph/setup.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
from catkin_pkg.python_setup import generate_distutils_setup
|
||||
|
||||
d = generate_distutils_setup(
|
||||
packages=['rosgraph', 'rosgraph.impl'],
|
||||
package_dir={'': 'src'},
|
||||
scripts=['scripts/rosgraph'],
|
||||
requires=['rospkg']
|
||||
)
|
||||
|
||||
setup(**d)
|
||||
56
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/__init__.py
vendored
Normal file
56
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/__init__.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
|
||||
from . rosenv import get_master_uri, ROS_MASTER_URI, ROS_NAMESPACE, ROS_HOSTNAME, ROS_IP, ROS_IPV6
|
||||
from . masterapi import Master, MasterFailure, MasterError, MasterException
|
||||
from . masterapi import is_online as is_master_online
|
||||
|
||||
# bring in names submodule
|
||||
from . import names
|
||||
|
||||
def myargv(argv=None):
|
||||
"""
|
||||
Remove ROS remapping arguments from sys.argv arguments.
|
||||
|
||||
:returns: copy of sys.argv with ROS remapping arguments removed, ``[str]``
|
||||
"""
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
return [a for a in argv if not names.REMAP in a]
|
||||
|
||||
__all__ = ['myargv',
|
||||
'get_master_uri', 'ROS_MASTER_URI', 'ROS_NAMESPACE', 'ROS_HOSTNAME', 'ROS_IP', 'ROS_IPV6',
|
||||
'Master', 'MasterFailure', 'MasterError', 'MasterException',
|
||||
'is_master_online']
|
||||
|
||||
34
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/impl/__init__.py
vendored
Normal file
34
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/impl/__init__.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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 $
|
||||
|
||||
582
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/impl/graph.py
vendored
Normal file
582
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/impl/graph.py
vendored
Normal file
@@ -0,0 +1,582 @@
|
||||
# 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$
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
"""
|
||||
Data structures and library for representing ROS Computation Graph state.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import itertools
|
||||
import random
|
||||
import logging
|
||||
import traceback
|
||||
try:
|
||||
from xmlrpc.client import ServerProxy
|
||||
except ImportError:
|
||||
from xmlrpclib import ServerProxy
|
||||
import socket
|
||||
|
||||
import rosgraph.masterapi
|
||||
|
||||
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.values()])
|
||||
|
||||
def has(self, edge):
|
||||
return edge in self
|
||||
|
||||
def __contains__(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.keys() if k.startswith(pref)]
|
||||
|
||||
pref = node+"|"
|
||||
edge_lists = matching(self.edges_by_start, pref) + matching(self.edges_by_end, 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 = rosgraph.masterapi.Master(_ROS_NAME)
|
||||
|
||||
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
|
||||
try:
|
||||
val = self.master.getSystemState()
|
||||
except rosgraph.masterapi.MasterException as e:
|
||||
print("Unable to contact master", str(e), file=sys.stderr)
|
||||
logger.error("unable to contact master: %s", str(e))
|
||||
return False
|
||||
|
||||
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 as 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:
|
||||
# #3579 bad node, ignore
|
||||
if len(info) < 5:
|
||||
continue
|
||||
|
||||
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 = ServerProxy(uri)
|
||||
updated = self._node_refresh_businfo(node, api, bad_node)
|
||||
except KeyError as 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:
|
||||
uri = self.master.lookupNode(node)
|
||||
except:
|
||||
msg = traceback.format_exc()
|
||||
logger.warn("master reported error in node lookup: %s"%msg)
|
||||
return None
|
||||
# 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
|
||||
|
||||
481
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/masterapi.py
vendored
Normal file
481
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/masterapi.py
vendored
Normal file
@@ -0,0 +1,481 @@
|
||||
# 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.
|
||||
"""
|
||||
|
||||
try:
|
||||
from xmlrpc.client import ServerProxy # Python 3.x
|
||||
except ImportError:
|
||||
from xmlrpclib import ServerProxy # Python 2.x
|
||||
|
||||
from . names import make_caller_id
|
||||
from . rosenv import get_master_uri
|
||||
from . network import parse_http_host_and_port
|
||||
|
||||
class MasterException(Exception):
|
||||
"""
|
||||
Base class of ROS-master related errors.
|
||||
"""
|
||||
pass
|
||||
|
||||
class MasterFailure(MasterException):
|
||||
"""
|
||||
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 MasterError(MasterException):
|
||||
"""
|
||||
Master returned an error code, which indicates an error in the
|
||||
arguments passed to the Master.
|
||||
"""
|
||||
pass
|
||||
|
||||
# backwards compat
|
||||
ROSMasterException = MasterException
|
||||
Error = MasterError
|
||||
Failure = MasterFailure
|
||||
|
||||
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('rosgraph', 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, ``str``
|
||||
:param master_uri: (optional) override default ROS master URI, ``str``
|
||||
:raises: :exc:`ValueError` If ROS master uri not set properly
|
||||
"""
|
||||
|
||||
if master_uri is None:
|
||||
master_uri = get_master_uri()
|
||||
self._reinit(master_uri)
|
||||
|
||||
self.caller_id = 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
|
||||
|
||||
:raises: :exc:`ValueError` If ROS master uri not set
|
||||
"""
|
||||
if master_uri is None:
|
||||
raise ValueError("ROS master URI is not set")
|
||||
# #1730 validate URL for better error messages
|
||||
try:
|
||||
parse_http_host_and_port(master_uri)
|
||||
except ValueError:
|
||||
raise ValueError("invalid master URI: %s"%(master_uri))
|
||||
|
||||
self.master_uri = master_uri
|
||||
self.handle = 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.
|
||||
|
||||
@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))
|
||||
329
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/names.py
vendored
Normal file
329
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/names.py
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
# 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: names.py 14589 2011-08-07 18:30:21Z kwc $
|
||||
|
||||
"""
|
||||
Library for manipulating ROS Names. See U{http://ros.org/wiki/Names}.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .rosenv import ROS_NAMESPACE
|
||||
|
||||
#TODO: why are these here?
|
||||
MSG_EXT = '.msg'
|
||||
SRV_EXT = '.srv'
|
||||
|
||||
SEP = '/'
|
||||
GLOBALNS = '/'
|
||||
PRIV_NAME = '~'
|
||||
REMAP = ":="
|
||||
ANYTYPE = '*'
|
||||
|
||||
if sys.hexversion > 0x03000000: #Python3
|
||||
def isstring(s):
|
||||
return isinstance(s, str) #Python 3.x
|
||||
else:
|
||||
def isstring(s):
|
||||
"""
|
||||
Small helper version to check an object is a string in a way that works
|
||||
for both Python 2 and 3
|
||||
"""
|
||||
return isinstance(s, basestring) #Python 2.x
|
||||
|
||||
def get_ros_namespace(env=None, argv=None):
|
||||
"""
|
||||
@param env: environment dictionary (defaults to os.environ)
|
||||
@type env: dict
|
||||
@param argv: command-line arguments (defaults to sys.argv)
|
||||
@type argv: [str]
|
||||
@return: ROS namespace of current program
|
||||
@rtype: str
|
||||
"""
|
||||
#we force command-line-specified namespaces to be globally scoped
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
for a in argv:
|
||||
if a.startswith('__ns:='):
|
||||
return make_global_ns(a[len('__ns:='):])
|
||||
if env is None:
|
||||
env = os.environ
|
||||
return make_global_ns(env.get(ROS_NAMESPACE, GLOBALNS))
|
||||
|
||||
def make_caller_id(name):
|
||||
"""
|
||||
Resolve a local name to the caller ID based on ROS environment settings (i.e. ROS_NAMESPACE)
|
||||
|
||||
@param name: local name to calculate caller ID from, e.g. 'camera', 'node'
|
||||
@type name: str
|
||||
@return: caller ID based on supplied local name
|
||||
@rtype: str
|
||||
"""
|
||||
return make_global_ns(ns_join(get_ros_namespace(), name))
|
||||
|
||||
def make_global_ns(name):
|
||||
"""
|
||||
Convert name to a global name with a trailing namespace separator.
|
||||
|
||||
@param name: ROS resource name. Cannot be a ~name.
|
||||
@type name: str
|
||||
@return str: name as a global name, e.g. 'foo' -> '/foo/'.
|
||||
This does NOT resolve a name.
|
||||
@rtype: str
|
||||
@raise ValueError: if name is a ~name
|
||||
"""
|
||||
if is_private(name):
|
||||
raise ValueError("cannot turn [%s] into a global name"%name)
|
||||
if not is_global(name):
|
||||
name = SEP + name
|
||||
if name[-1] != SEP:
|
||||
name = name + SEP
|
||||
return name
|
||||
|
||||
def is_global(name):
|
||||
"""
|
||||
Test if name is a global graph resource name.
|
||||
|
||||
@param name: must be a legal name in canonical form
|
||||
@type name: str
|
||||
@return: True if name is a globally referenced name (i.e. /ns/name)
|
||||
@rtype: bool
|
||||
"""
|
||||
return name and name[0] == SEP
|
||||
|
||||
def is_private(name):
|
||||
"""
|
||||
Test if name is a private graph resource name.
|
||||
|
||||
@param name: must be a legal name in canonical form
|
||||
@type name: str
|
||||
@return bool: True if name is a privately referenced name (i.e. ~name)
|
||||
"""
|
||||
return name and name[0] == PRIV_NAME
|
||||
|
||||
def namespace(name):
|
||||
"""
|
||||
Get the namespace of name. The namespace is returned with a
|
||||
trailing slash in order to favor easy concatenation and easier use
|
||||
within the global context.
|
||||
|
||||
@param name: name to return the namespace of. Must be a legal
|
||||
name. NOTE: an empty name will return the global namespace.
|
||||
@type name: str
|
||||
@return str: Namespace of name. For example, '/wg/node1' returns '/wg/'. The
|
||||
global namespace is '/'.
|
||||
@rtype: str
|
||||
@raise ValueError: if name is invalid
|
||||
"""
|
||||
"map name to its namespace"
|
||||
if name is None:
|
||||
raise ValueError('name')
|
||||
if not isstring(name):
|
||||
raise TypeError('name')
|
||||
if not name:
|
||||
return SEP
|
||||
elif name[-1] == SEP:
|
||||
name = name[:-1]
|
||||
return name[:name.rfind(SEP)+1] or SEP
|
||||
|
||||
def ns_join(ns, name):
|
||||
"""
|
||||
Join a namespace and name. If name is unjoinable (i.e. ~private or
|
||||
/global) it will be returned without joining
|
||||
|
||||
@param ns: namespace ('/' and '~' are both legal). If ns is the empty string, name will be returned.
|
||||
@type ns: str
|
||||
@param name str: a legal name
|
||||
@return str: name concatenated to ns, or name if it is
|
||||
unjoinable.
|
||||
@rtype: str
|
||||
"""
|
||||
if is_private(name) or is_global(name):
|
||||
return name
|
||||
if ns == PRIV_NAME:
|
||||
return PRIV_NAME + name
|
||||
if not ns:
|
||||
return name
|
||||
if ns[-1] == SEP:
|
||||
return ns + name
|
||||
return ns + SEP + name
|
||||
|
||||
def load_mappings(argv):
|
||||
"""
|
||||
Load name mappings encoded in command-line arguments. This will filter
|
||||
out any parameter assignment mappings.
|
||||
|
||||
@param argv: command-line arguments
|
||||
@type argv: [str]
|
||||
@return: name->name remappings.
|
||||
@rtype: dict {str: str}
|
||||
"""
|
||||
mappings = {}
|
||||
for arg in argv:
|
||||
if REMAP in arg:
|
||||
try:
|
||||
src, dst = [x.strip() for x in arg.split(REMAP)]
|
||||
if src and dst:
|
||||
if len(src) > 1 and src[0] == '_' and src[1] != '_':
|
||||
#ignore parameter assignment mappings
|
||||
pass
|
||||
else:
|
||||
mappings[src] = dst
|
||||
except:
|
||||
#TODO: remove
|
||||
sys.stderr.write("ERROR: Invalid remapping argument '%s'\n"%arg)
|
||||
return mappings
|
||||
|
||||
|
||||
################################################################################
|
||||
# NAME VALIDATORS
|
||||
|
||||
import re
|
||||
|
||||
#~,/, or ascii char followed by (alphanumeric, _, /)
|
||||
NAME_LEGAL_CHARS_P = re.compile('^[\~\/A-Za-z][\w\/]*$')
|
||||
def is_legal_name(name):
|
||||
"""
|
||||
Check if name is a legal ROS name for graph resources
|
||||
(alphabetical character followed by alphanumeric, underscore, or
|
||||
forward slashes). This constraint is currently not being enforced,
|
||||
but may start getting enforced in later versions of ROS.
|
||||
|
||||
@param name: Name
|
||||
@type name: str
|
||||
"""
|
||||
# should we enforce unicode checks?
|
||||
if name is None:
|
||||
return False
|
||||
# empty string is a legal name as it resolves to namespace
|
||||
if name == '':
|
||||
return True
|
||||
m = NAME_LEGAL_CHARS_P.match(name)
|
||||
return m is not None and m.group(0) == name and not '//' in name
|
||||
|
||||
BASE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w]*$') #ascii char followed by (alphanumeric, _)
|
||||
def is_legal_base_name(name):
|
||||
"""
|
||||
Validates that name is a legal base name for a graph resource. A base name has
|
||||
no namespace context, e.g. "node_name".
|
||||
"""
|
||||
if name is None:
|
||||
return False
|
||||
m = BASE_NAME_LEGAL_CHARS_P.match(name)
|
||||
return m is not None and m.group(0) == name
|
||||
|
||||
def canonicalize_name(name):
|
||||
"""
|
||||
Put name in canonical form. Extra slashes '//' are removed and
|
||||
name is returned without any trailing slash, e.g. /foo/bar
|
||||
@param name: ROS name
|
||||
@type name: str
|
||||
"""
|
||||
if not name or name == SEP:
|
||||
return name
|
||||
elif name[0] == SEP:
|
||||
return '/' + '/'.join([x for x in name.split(SEP) if x])
|
||||
else:
|
||||
return '/'.join([x for x in name.split(SEP) if x])
|
||||
|
||||
def resolve_name(name, namespace_, remappings=None):
|
||||
"""
|
||||
Resolve a ROS name to its global, canonical form. Private ~names
|
||||
are resolved relative to the node name.
|
||||
|
||||
@param name: name to resolve.
|
||||
@type name: str
|
||||
@param namespace_: node name to resolve relative to.
|
||||
@type namespace_: str
|
||||
@param remappings: Map of resolved remappings. Use None to indicate no remapping.
|
||||
@return: Resolved name. If name is empty/None, resolve_name
|
||||
returns parent namespace_. If namespace_ is empty/None,
|
||||
@rtype: str
|
||||
"""
|
||||
if not name: #empty string resolves to parent of the namespace_
|
||||
return namespace(namespace_)
|
||||
|
||||
name = canonicalize_name(name)
|
||||
if name[0] == SEP: #global name
|
||||
resolved_name = name
|
||||
elif is_private(name): #~name
|
||||
# #3044: be careful not to accidentally make rest of name global
|
||||
resolved_name = canonicalize_name(namespace_ + SEP + name[1:])
|
||||
else: #relative
|
||||
resolved_name = namespace(namespace_) + name
|
||||
|
||||
#Mappings override general namespace-based resolution
|
||||
# - do this before canonicalization as remappings are meant to
|
||||
# match the name as specified in the code
|
||||
if remappings and resolved_name in remappings:
|
||||
return remappings[resolved_name]
|
||||
else:
|
||||
return resolved_name
|
||||
|
||||
def script_resolve_name(script_name, name):
|
||||
"""
|
||||
Name resolver for scripts. Supports :envvar:`ROS_NAMESPACE`. Does not
|
||||
support remapping arguments.
|
||||
|
||||
:param name: name to resolve, ``str``
|
||||
:param script_name: name of script. script_name must not
|
||||
contain a namespace., ``str``
|
||||
:returns: resolved name, ``str``
|
||||
"""
|
||||
if not name: #empty string resolves to namespace
|
||||
return get_ros_namespace()
|
||||
#Check for global name: /foo/name resolves to /foo/name
|
||||
if is_global(name):
|
||||
return name
|
||||
#Check for private name: ~name resolves to /caller_id/name
|
||||
elif is_private(name):
|
||||
return ns_join(make_caller_id(script_name), name[1:])
|
||||
return get_ros_namespace() + name
|
||||
|
||||
def anonymous_name(id):
|
||||
"""
|
||||
Generate a ROS-legal 'anonymous' name
|
||||
|
||||
@param id: prefix for anonymous name
|
||||
@type id: str
|
||||
"""
|
||||
import socket, random
|
||||
name = "%s_%s_%s_%s"%(id, socket.gethostname(), os.getpid(), random.randint(0, sys.maxsize))
|
||||
# RFC 952 allows hyphens, IP addrs can have '.'s, both
|
||||
# of which are illegal for ROS names. For good
|
||||
# measure, screen ipv6 ':'.
|
||||
name = name.replace('.', '_')
|
||||
name = name.replace('-', '_')
|
||||
return name.replace(':', '_')
|
||||
|
||||
424
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/network.py
vendored
Normal file
424
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/network.py
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
# 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: network.py 15125 2011-10-06 02:51:15Z kwc $
|
||||
|
||||
"""
|
||||
Network APIs for ROS-based systems, including IP address and ROS
|
||||
TCP header libraries. Because ROS-based runtimes must respect the
|
||||
ROS_IP and ROS_HOSTNAME environment variables, ROS-specific APIs
|
||||
are necessary for correctly retrieving local IP address
|
||||
information.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import platform
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO #Python 2.x
|
||||
python3 = 0
|
||||
except ImportError:
|
||||
from io import BytesIO #Python 3.x
|
||||
python3 = 1
|
||||
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
from .rosenv import ROS_IP, ROS_HOSTNAME, ROS_IPV6
|
||||
|
||||
SIOCGIFCONF = 0x8912
|
||||
SIOCGIFADDR = 0x8915
|
||||
if platform.system() == 'FreeBSD':
|
||||
SIOCGIFADDR = 0xc0206921
|
||||
if platform.architecture()[0] == '64bit':
|
||||
SIOCGIFCONF = 0xc0106924
|
||||
else:
|
||||
SIOCGIFCONF = 0xc0086924
|
||||
|
||||
logger = logging.getLogger('rosgraph.network')
|
||||
|
||||
def parse_http_host_and_port(url):
|
||||
"""
|
||||
Convenience routine to handle parsing and validation of HTTP URL
|
||||
port due to the fact that Python only provides easy accessors in
|
||||
Python 2.5 and later. Validation checks that the protocol and host
|
||||
are set.
|
||||
|
||||
:param url: URL to parse, ``str``
|
||||
:returns: hostname and port number in URL or 80 (default), ``(str, int)``
|
||||
:raises: :exc:`ValueError` If the url does not validate
|
||||
"""
|
||||
# can't use p.port because that's only available in Python 2.5
|
||||
if not url:
|
||||
raise ValueError('not a valid URL')
|
||||
p = urlparse.urlparse(url)
|
||||
if not p[0] or not p[1]: #protocol and host
|
||||
raise ValueError('not a valid URL')
|
||||
if ':' in p[1]:
|
||||
hostname, port = p[1].split(':')
|
||||
port = int(port)
|
||||
else:
|
||||
hostname, port = p[1], 80
|
||||
return hostname, port
|
||||
|
||||
def _is_unix_like_platform():
|
||||
"""
|
||||
:returns: true if the platform conforms to UNIX/POSIX-style APIs
|
||||
@rtype: bool
|
||||
"""
|
||||
return platform.system() in ['Linux', 'FreeBSD', 'Darwin']
|
||||
|
||||
def get_address_override():
|
||||
"""
|
||||
:returns: ROS_IP/ROS_HOSTNAME override or None, ``str``
|
||||
:raises: :exc:`ValueError` If ROS_IP/ROS_HOSTNAME/__ip/__hostname are invalidly specified
|
||||
"""
|
||||
# #998: check for command-line remappings first
|
||||
# TODO IPV6: check for compatibility
|
||||
for arg in sys.argv:
|
||||
if arg.startswith('__hostname:=') or arg.startswith('__ip:='):
|
||||
try:
|
||||
_, val = arg.split(':=')
|
||||
return val
|
||||
except: #split didn't unpack properly
|
||||
raise ValueError("invalid ROS command-line remapping argument '%s'"%arg)
|
||||
|
||||
# check ROS_HOSTNAME and ROS_IP environment variables, which are
|
||||
# aliases for each other
|
||||
if ROS_HOSTNAME in os.environ:
|
||||
hostname = os.environ[ROS_HOSTNAME]
|
||||
if hostname == '':
|
||||
msg = 'invalid ROS_HOSTNAME (an empty string)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
else:
|
||||
parts = urlparse.urlparse(hostname)
|
||||
if parts.scheme:
|
||||
msg = 'invalid ROS_HOSTNAME (protocol ' + ('and port ' if parts.port else '') + 'should not be included)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
elif hostname.find(':') != -1:
|
||||
# this can not be checked with urlparse()
|
||||
# since it does not extract the port for a hostname like "foo:1234"
|
||||
msg = 'invalid ROS_HOSTNAME (port should not be included)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
return hostname
|
||||
elif ROS_IP in os.environ:
|
||||
ip = os.environ[ROS_IP]
|
||||
if ip == '':
|
||||
msg = 'invalid ROS_IP (an empty string)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
elif ip.find('://') != -1:
|
||||
msg = 'invalid ROS_IP (protocol should not be included)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
elif ip.find('.') != -1 and ip.rfind(':') > ip.rfind('.'):
|
||||
msg = 'invalid ROS_IP (port should not be included)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
elif ip.find('.') == -1 and ip.find(':') == -1:
|
||||
msg = 'invalid ROS_IP (must be a valid IPv4 or IPv6 address)'
|
||||
sys.stderr.write(msg + '\n')
|
||||
logger.warn(msg)
|
||||
return ip
|
||||
return None
|
||||
|
||||
def is_local_address(hostname):
|
||||
"""
|
||||
:param hostname: host name/address, ``str``
|
||||
:returns True: if hostname maps to a local address, False otherwise. False conditions include invalid hostnames.
|
||||
"""
|
||||
try:
|
||||
if use_ipv6():
|
||||
reverse_ips = [host[4][0] for host in socket.getaddrinfo(hostname, 0, 0, 0, socket.SOL_TCP)]
|
||||
else:
|
||||
reverse_ips = [host[4][0] for host in socket.getaddrinfo(hostname, 0, socket.AF_INET, 0, socket.SOL_TCP)]
|
||||
except socket.error:
|
||||
return False
|
||||
local_addresses = ['localhost'] + get_local_addresses()
|
||||
# 127. check is due to #1260
|
||||
if ([ip for ip in reverse_ips if (ip.startswith('127.') or ip == '::1')] != []) or (set(reverse_ips) & set(local_addresses) != set()):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_local_address():
|
||||
"""
|
||||
:returns: default local IP address (e.g. eth0). May be overriden by ROS_IP/ROS_HOSTNAME/__ip/__hostname, ``str``
|
||||
"""
|
||||
override = get_address_override()
|
||||
if override:
|
||||
return override
|
||||
addrs = get_local_addresses()
|
||||
if len(addrs) == 1:
|
||||
return addrs[0]
|
||||
for addr in addrs:
|
||||
# pick first non 127/8 address
|
||||
if not addr.startswith('127.') and not addr == '::1':
|
||||
return addr
|
||||
else: # loopback
|
||||
if use_ipv6():
|
||||
return '::1'
|
||||
else:
|
||||
return '127.0.0.1'
|
||||
|
||||
# cache for performance reasons
|
||||
_local_addrs = None
|
||||
def get_local_addresses():
|
||||
"""
|
||||
:returns: known local addresses. Not affected by ROS_IP/ROS_HOSTNAME, ``[str]``
|
||||
"""
|
||||
# cache address data as it can be slow to calculate
|
||||
global _local_addrs
|
||||
if _local_addrs is not None:
|
||||
return _local_addrs
|
||||
|
||||
local_addrs = None
|
||||
if _is_unix_like_platform():
|
||||
# unix-only branch
|
||||
v4addrs = []
|
||||
v6addrs = []
|
||||
import netifaces
|
||||
for iface in netifaces.interfaces():
|
||||
try:
|
||||
ifaddrs = netifaces.ifaddresses(iface)
|
||||
except ValueError:
|
||||
# even if interfaces() returns an interface name
|
||||
# ifaddresses() might raise a ValueError
|
||||
# https://bugs.launchpad.net/ubuntu/+source/netifaces/+bug/753009
|
||||
continue
|
||||
if socket.AF_INET in ifaddrs:
|
||||
v4addrs.extend([addr['addr'] for addr in ifaddrs[socket.AF_INET]])
|
||||
if socket.AF_INET6 in ifaddrs:
|
||||
v6addrs.extend([addr['addr'] for addr in ifaddrs[socket.AF_INET6]])
|
||||
if use_ipv6():
|
||||
local_addrs = v6addrs + v4addrs
|
||||
else:
|
||||
local_addrs = v4addrs
|
||||
else:
|
||||
# cross-platform branch, can only resolve one address
|
||||
if use_ipv6():
|
||||
local_addrs = [host[4][0] for host in socket.getaddrinfo(socket.gethostname(), 0, 0, 0, socket.SOL_TCP)]
|
||||
else:
|
||||
local_addrs = [host[4][0] for host in socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, socket.SOL_TCP)]
|
||||
_local_addrs = local_addrs
|
||||
return local_addrs
|
||||
|
||||
def use_ipv6():
|
||||
return ROS_IPV6 in os.environ and os.environ[ROS_IPV6] == 'on'
|
||||
|
||||
def get_bind_address(address=None):
|
||||
"""
|
||||
:param address: (optional) address to compare against, ``str``
|
||||
:returns: address TCP/IP sockets should use for binding. This is
|
||||
generally 0.0.0.0, but if \a address or ROS_IP/ROS_HOSTNAME is set
|
||||
to localhost it will return 127.0.0.1, ``str``
|
||||
"""
|
||||
if address is None:
|
||||
address = get_address_override()
|
||||
if address and (address == 'localhost' or address.startswith('127.') or address == '::1' ):
|
||||
#localhost or 127/8
|
||||
if use_ipv6():
|
||||
return '::1'
|
||||
elif address.startswith('127.'):
|
||||
return address
|
||||
else:
|
||||
return '127.0.0.1' #loopback
|
||||
else:
|
||||
if use_ipv6():
|
||||
return '::'
|
||||
else:
|
||||
return '0.0.0.0'
|
||||
|
||||
# #528: semi-complicated logic for determining XML-RPC URI
|
||||
def get_host_name():
|
||||
"""
|
||||
Determine host-name for use in host-name-based addressing (e.g. XML-RPC URIs):
|
||||
- if ROS_IP/ROS_HOSTNAME is set, use that address
|
||||
- if the hostname returns a non-localhost value, use that
|
||||
- use whatever L{get_local_address()} returns
|
||||
"""
|
||||
hostname = get_address_override()
|
||||
if not hostname:
|
||||
try:
|
||||
hostname = socket.gethostname()
|
||||
except:
|
||||
pass
|
||||
if not hostname or hostname == 'localhost' or hostname.startswith('127.'):
|
||||
hostname = get_local_address()
|
||||
return hostname
|
||||
|
||||
def create_local_xmlrpc_uri(port):
|
||||
"""
|
||||
Determine the XMLRPC URI for local servers. This handles the search
|
||||
logic of checking ROS environment variables, the known hostname,
|
||||
and local interface IP addresses to determine the best possible
|
||||
URI.
|
||||
|
||||
:param port: port that server is running on, ``int``
|
||||
:returns: XMLRPC URI, ``str``
|
||||
"""
|
||||
#TODO: merge logic in rosgraph.xmlrpc with this routine
|
||||
# in the future we may not want to be locked to http protocol nor root path
|
||||
return 'http://%s:%s/'%(get_host_name(), port)
|
||||
|
||||
|
||||
## handshake utils ###########################################
|
||||
|
||||
class ROSHandshakeException(Exception):
|
||||
"""
|
||||
Exception to represent errors decoding handshake
|
||||
"""
|
||||
pass
|
||||
|
||||
def decode_ros_handshake_header(header_str):
|
||||
"""
|
||||
Decode serialized ROS handshake header into a Python dictionary
|
||||
|
||||
header is a list of string key=value pairs, each prefixed by a
|
||||
4-byte length field. It is preceeded by a 4-byte length field for
|
||||
the entire header.
|
||||
|
||||
:param header_str: encoded header string. May contain extra data at the end, ``str``
|
||||
:returns: key value pairs encoded in \a header_str, ``{str: str}``
|
||||
"""
|
||||
(size, ) = struct.unpack('<I', header_str[0:4])
|
||||
size += 4 # add in 4 to include size of size field
|
||||
header_len = len(header_str)
|
||||
if size > header_len:
|
||||
raise ROSHandshakeException("Incomplete header. Expected %s bytes but only have %s"%((size+4), header_len))
|
||||
|
||||
d = {}
|
||||
start = 4
|
||||
while start < size:
|
||||
(field_size, ) = struct.unpack('<I', header_str[start:start+4])
|
||||
if field_size == 0:
|
||||
raise ROSHandshakeException("Invalid 0-length handshake header field")
|
||||
start += field_size + 4
|
||||
if start > size:
|
||||
raise ROSHandshakeException("Invalid line length in handshake header: %s"%size)
|
||||
line = header_str[start-field_size:start]
|
||||
|
||||
#python3 compatibility
|
||||
if python3 == 1:
|
||||
line = line.decode()
|
||||
|
||||
idx = line.find("=")
|
||||
if idx < 0:
|
||||
raise ROSHandshakeException("Invalid line in handshake header: [%s]"%line)
|
||||
key = line[:idx]
|
||||
value = line[idx+1:]
|
||||
d[key.strip()] = value
|
||||
return d
|
||||
|
||||
def read_ros_handshake_header(sock, b, buff_size):
|
||||
"""
|
||||
Read in tcpros header off the socket \a sock using buffer \a b.
|
||||
|
||||
:param sock: socket must be in blocking mode, ``socket``
|
||||
:param b: buffer to use, ``StringIO`` for Python2, ``BytesIO`` for Python 3
|
||||
:param buff_size: incoming buffer size to use, ``int``
|
||||
:returns: key value pairs encoded in handshake, ``{str: str}``
|
||||
:raises: :exc:`ROSHandshakeException` If header format does not match expected
|
||||
"""
|
||||
header_str = None
|
||||
while not header_str:
|
||||
d = sock.recv(buff_size)
|
||||
if not d:
|
||||
raise ROSHandshakeException("connection from sender terminated before handshake header received. %s bytes were received. Please check sender for additional details."%b.tell())
|
||||
b.write(d)
|
||||
btell = b.tell()
|
||||
if btell > 4:
|
||||
# most likely we will get the full header in the first recv, so
|
||||
# not worth tiny optimizations possible here
|
||||
bval = b.getvalue()
|
||||
(size,) = struct.unpack('<I', bval[0:4])
|
||||
if btell - 4 >= size:
|
||||
header_str = bval
|
||||
|
||||
# memmove the remnants of the buffer back to the start
|
||||
leftovers = bval[size+4:]
|
||||
b.truncate(len(leftovers))
|
||||
b.seek(0)
|
||||
b.write(leftovers)
|
||||
header_recvd = True
|
||||
|
||||
# process the header
|
||||
return decode_ros_handshake_header(bval)
|
||||
|
||||
def encode_ros_handshake_header(header):
|
||||
"""
|
||||
Encode ROS handshake header as a byte string. Each header
|
||||
field is a string key value pair. The encoded header is
|
||||
prefixed by a length field, as is each field key/value pair.
|
||||
key/value pairs a separated by a '=' equals sign.
|
||||
|
||||
FORMAT: (4-byte length + [4-byte field length + field=value ]*)
|
||||
|
||||
:param header: header field keys/values, ``dict``
|
||||
:returns: header encoded as byte string, ``bytes``
|
||||
"""
|
||||
str_cls = str if python3 else unicode
|
||||
|
||||
# encode all unicode keys in the header. Ideally, the type of these would be specified by the api
|
||||
encoded_header = {}
|
||||
for k, v in header.items():
|
||||
if isinstance(k, str_cls):
|
||||
k = k.encode('utf-8')
|
||||
if isinstance(v, str_cls):
|
||||
v = v.encode('utf-8')
|
||||
encoded_header[k] = v
|
||||
|
||||
fields = [k + b"=" + v for k, v in sorted(encoded_header.items())]
|
||||
s = b''.join([struct.pack('<I', len(f)) + f for f in fields])
|
||||
|
||||
return struct.pack('<I', len(s)) + s
|
||||
|
||||
def write_ros_handshake_header(sock, header):
|
||||
"""
|
||||
Write ROS handshake header header to socket sock
|
||||
|
||||
:param sock: socket to write to (must be in blocking mode), ``socket.socket``
|
||||
:param header: header field keys/values, ``{str : str}``
|
||||
:returns: Number of bytes sent (for statistics), ``int``
|
||||
"""
|
||||
s = encode_ros_handshake_header(header)
|
||||
sock.sendall(s)
|
||||
return len(s) #STATS
|
||||
|
||||
75
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/rosenv.py
vendored
Normal file
75
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/rosenv.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROS_MASTER_URI = "ROS_MASTER_URI"
|
||||
|
||||
ROS_IP ="ROS_IP"
|
||||
ROS_IPV6 ="ROS_IPV6"
|
||||
ROS_HOSTNAME ="ROS_HOSTNAME"
|
||||
ROS_NAMESPACE ="ROS_NAMESPACE"
|
||||
|
||||
def get_master_uri(env=None, argv=None):
|
||||
"""
|
||||
Get the :envvar:`ROS_MASTER_URI` setting from the command-line args or
|
||||
environment, command-line args takes precedence.
|
||||
|
||||
:param env: override environment dictionary, ``dict``
|
||||
:param argv: override ``sys.argv``, ``[str]``
|
||||
:raises: :exc:`ValueError` If :envvar:`ROS_MASTER_URI` value is invalidly
|
||||
specified
|
||||
"""
|
||||
if env is None:
|
||||
env = os.environ
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
try:
|
||||
for arg in argv:
|
||||
if arg.startswith('__master:='):
|
||||
val = None
|
||||
try:
|
||||
_, val = arg.split(':=')
|
||||
except:
|
||||
pass
|
||||
|
||||
# we ignore required here because there really is no
|
||||
# correct return value as the configuration is bad
|
||||
# rather than unspecified
|
||||
if not val:
|
||||
raise ValueError("__master remapping argument '%s' improperly specified"%arg)
|
||||
return val
|
||||
return env[ROS_MASTER_URI]
|
||||
except KeyError as e:
|
||||
return None
|
||||
|
||||
103
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/rosgraph_main.py
vendored
Normal file
103
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/rosgraph_main.py
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/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.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
from . import roslogging
|
||||
from . import masterapi
|
||||
|
||||
from .impl import graph
|
||||
|
||||
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' or sys.argv[1] == '--help'):
|
||||
fullusage()
|
||||
return
|
||||
else:
|
||||
fullusage()
|
||||
sys.exit(-1)
|
||||
|
||||
roslogging.configure_logging('rosgraph')
|
||||
|
||||
# make sure master is available
|
||||
master = masterapi.Master('rosgraph')
|
||||
try:
|
||||
master.getPid()
|
||||
except:
|
||||
print("ERROR: Unable to communicate with master!", file=sys.stderr)
|
||||
return
|
||||
|
||||
g = 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:
|
||||
prefix = n + '|'
|
||||
print(' ' + n + ' :')
|
||||
print(' Inbound:')
|
||||
for k in g.nn_edges.edges_by_end.keys():
|
||||
if k.startswith(prefix):
|
||||
for c in g.nn_edges.edges_by_end[k]:
|
||||
print(' ' + c.start)
|
||||
print(' Outbound:')
|
||||
for k in g.nn_edges.edges_by_start.keys():
|
||||
if k.startswith(prefix):
|
||||
for c in g.nn_edges.edges_by_start[k]:
|
||||
print(' ' + c.end)
|
||||
if g.srvs:
|
||||
print('Services:')
|
||||
for s in g.srvs:
|
||||
print(' ' + s)
|
||||
|
||||
time.sleep(1.0)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
212
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/roslogging.py
vendored
Normal file
212
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/roslogging.py
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Library for configuring python logging to standard ROS locations (e.g. ROS_LOG_DIR).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
import rospkg
|
||||
from rospkg.environment import ROS_LOG_DIR
|
||||
|
||||
class LoggingException(Exception): pass
|
||||
|
||||
def renew_latest_logdir(logfile_dir):
|
||||
log_dir = os.path.dirname(logfile_dir)
|
||||
latest_dir = os.path.join(log_dir, 'latest')
|
||||
if os.path.lexists(latest_dir):
|
||||
if not os.path.islink(latest_dir):
|
||||
return False
|
||||
os.remove(latest_dir)
|
||||
os.symlink(logfile_dir, latest_dir)
|
||||
return True
|
||||
|
||||
def configure_logging(logname, level=logging.INFO, filename=None, env=None):
|
||||
"""
|
||||
Configure Python logging package to send log files to ROS-specific log directory
|
||||
:param logname str: name of logger, ``str``
|
||||
:param filename: filename to log to. If not set, a log filename
|
||||
will be generated using logname, ``str``
|
||||
:param env: override os.environ dictionary, ``dict``
|
||||
:returns: log file name, ``str``
|
||||
:raises: :exc:`LoggingException` If logging cannot be configured as specified
|
||||
"""
|
||||
if env is None:
|
||||
env = os.environ
|
||||
|
||||
logname = logname or 'unknown'
|
||||
log_dir = rospkg.get_log_dir(env=env)
|
||||
|
||||
# if filename is not explicitly provided, generate one using logname
|
||||
if not filename:
|
||||
log_filename = os.path.join(log_dir, '%s-%s.log'%(logname, os.getpid()))
|
||||
else:
|
||||
log_filename = os.path.join(log_dir, filename)
|
||||
|
||||
logfile_dir = os.path.dirname(log_filename)
|
||||
if not os.path.exists(logfile_dir):
|
||||
try:
|
||||
makedirs_with_parent_perms(logfile_dir)
|
||||
except OSError:
|
||||
# cannot print to screen because command-line tools with output use this
|
||||
if os.path.exists(logfile_dir):
|
||||
# We successfully created the logging folder, but could not change
|
||||
# permissions of the new folder to the same as the parent folder
|
||||
sys.stderr.write("WARNING: Could not change permissions for folder [%s], make sure that the parent folder has correct permissions.\n"%logfile_dir)
|
||||
else:
|
||||
# Could not create folder
|
||||
sys.stderr.write("WARNING: cannot create log directory [%s]. Please set %s to a writable location.\n"%(logfile_dir, ROS_LOG_DIR))
|
||||
return None
|
||||
elif os.path.isfile(logfile_dir):
|
||||
raise LoggingException("Cannot save log files: file [%s] is in the way"%logfile_dir)
|
||||
|
||||
# the log dir itself should not be symlinked as latest
|
||||
if logfile_dir != log_dir:
|
||||
if sys.platform not in ['win32']:
|
||||
try:
|
||||
success = renew_latest_logdir(logfile_dir)
|
||||
if not success:
|
||||
sys.stderr.write("INFO: cannot create a symlink to latest log directory\n")
|
||||
except OSError as e:
|
||||
sys.stderr.write("INFO: cannot create a symlink to latest log directory: %s\n" % e)
|
||||
|
||||
if 'ROS_PYTHON_LOG_CONFIG_FILE' in os.environ:
|
||||
config_file = os.environ['ROS_PYTHON_LOG_CONFIG_FILE']
|
||||
else:
|
||||
# search for logging config file in /etc/. If it's not there,
|
||||
# look for it package-relative.
|
||||
fname = 'python_logging.conf'
|
||||
rosgraph_d = rospkg.RosPack().get_path('rosgraph')
|
||||
for f in [os.path.join(rospkg.get_ros_home(), 'config', fname),
|
||||
'/etc/ros/%s'%(fname),
|
||||
os.path.join(rosgraph_d, 'conf', fname)]:
|
||||
if os.path.isfile(f):
|
||||
config_file = f
|
||||
break
|
||||
else:
|
||||
config_file = None
|
||||
|
||||
if config_file is None or not os.path.isfile(config_file):
|
||||
# logging is considered soft-fail
|
||||
sys.stderr.write("WARNING: cannot load logging configuration file, logging is disabled\n")
|
||||
logging.getLogger(logname).setLevel(logging.CRITICAL)
|
||||
return log_filename
|
||||
|
||||
# pass in log_filename as argument to pylogging.conf
|
||||
os.environ['ROS_LOG_FILENAME'] = log_filename
|
||||
# #3625: disabling_existing_loggers=False
|
||||
logging.config.fileConfig(config_file, disable_existing_loggers=False)
|
||||
return log_filename
|
||||
|
||||
def makedirs_with_parent_perms(p):
|
||||
"""
|
||||
Create the directory using the permissions of the nearest
|
||||
(existing) parent directory. This is useful for logging, where a
|
||||
root process sometimes has to log in the user's space.
|
||||
:param p: directory to create, ``str``
|
||||
"""
|
||||
p = os.path.abspath(p)
|
||||
parent = os.path.dirname(p)
|
||||
# recurse upwards, checking to make sure we haven't reached the
|
||||
# top
|
||||
if not os.path.exists(p) and p and parent != p:
|
||||
makedirs_with_parent_perms(parent)
|
||||
s = os.stat(parent)
|
||||
os.mkdir(p)
|
||||
|
||||
# if perms of new dir don't match, set anew
|
||||
s2 = os.stat(p)
|
||||
if s.st_uid != s2.st_uid or s.st_gid != s2.st_gid:
|
||||
os.chown(p, s.st_uid, s.st_gid)
|
||||
if s.st_mode != s2.st_mode:
|
||||
os.chmod(p, s.st_mode)
|
||||
|
||||
_logging_to_rospy_names = {
|
||||
'DEBUG': ('DEBUG', '\033[32m'),
|
||||
'INFO': ('INFO', None),
|
||||
'WARNING': ('WARN', '\033[33m'),
|
||||
'ERROR': ('ERROR', '\033[31m'),
|
||||
'CRITICAL': ('FATAL', '\033[31m')
|
||||
}
|
||||
_color_reset = '\033[0m'
|
||||
_defaultFormatter = logging.Formatter()
|
||||
|
||||
class RosStreamHandler(logging.Handler):
|
||||
def __init__(self, colorize=True):
|
||||
super(RosStreamHandler, self).__init__()
|
||||
self._colorize = colorize
|
||||
try:
|
||||
from rospy.rostime import get_time, is_wallclock
|
||||
self._get_time = get_time
|
||||
self._is_wallclock = is_wallclock
|
||||
except ImportError:
|
||||
self._get_time = None
|
||||
self._is_wallclock = None
|
||||
|
||||
def emit(self, record):
|
||||
level, color = _logging_to_rospy_names[record.levelname]
|
||||
record_message = _defaultFormatter.format(record)
|
||||
msg = os.environ.get(
|
||||
'ROSCONSOLE_FORMAT', '[${severity}] [${time}]: ${message}')
|
||||
msg = msg.replace('${severity}', level)
|
||||
msg = msg.replace('${message}', str(record_message))
|
||||
msg = msg.replace('${walltime}', '%f' % time.time())
|
||||
msg = msg.replace('${thread}', str(record.thread))
|
||||
msg = msg.replace('${logger}', str(record.name))
|
||||
msg = msg.replace('${file}', str(record.pathname))
|
||||
msg = msg.replace('${line}', str(record.lineno))
|
||||
msg = msg.replace('${function}', str(record.funcName))
|
||||
try:
|
||||
from rospy import get_name
|
||||
node_name = get_name()
|
||||
except ImportError:
|
||||
node_name = '<unknown_node_name>'
|
||||
msg = msg.replace('${node}', node_name)
|
||||
time_str = '%f' % time.time()
|
||||
if self._get_time is not None and not self._is_wallclock():
|
||||
time_str += ', %f' % self._get_time()
|
||||
msg = msg.replace('${time}', time_str)
|
||||
msg += '\n'
|
||||
if record.levelno < logging.WARNING:
|
||||
self._write(sys.stdout, msg, color)
|
||||
else:
|
||||
self._write(sys.stderr, msg, color)
|
||||
|
||||
def _write(self, fd, msg, color):
|
||||
if self._colorize and color and hasattr(fd, 'isatty') and fd.isatty():
|
||||
msg = color + msg + _color_reset
|
||||
fd.write(msg)
|
||||
297
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/xmlrpc.py
vendored
Normal file
297
thirdparty/ros/ros_comm/tools/rosgraph/src/rosgraph/xmlrpc.py
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
# 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: xmlrpc.py 15336 2011-11-07 20:43:00Z kwc $
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
"""
|
||||
Common XML-RPC for higher-level libraries running XML-RPC libraries in
|
||||
ROS. In particular, this library provides common handling for URI
|
||||
calculation based on ROS environment variables.
|
||||
|
||||
The common entry point for most libraries is the L{XmlRpcNode} class.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import select
|
||||
import socket
|
||||
|
||||
try:
|
||||
import _thread
|
||||
except ImportError:
|
||||
import thread as _thread
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler #Python 3.x
|
||||
except ImportError:
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer #Python 2.x
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler #Python 2.x
|
||||
|
||||
try:
|
||||
import socketserver
|
||||
except ImportError:
|
||||
import SocketServer as socketserver
|
||||
|
||||
import rosgraph.network
|
||||
|
||||
def isstring(s):
|
||||
"""Small helper version to check an object is a string in a way that works
|
||||
for both Python 2 and 3
|
||||
"""
|
||||
try:
|
||||
return isinstance(s, basestring)
|
||||
except NameError:
|
||||
return isinstance(s, str)
|
||||
|
||||
class SilenceableXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
def log_message(self, format, *args):
|
||||
if 0:
|
||||
SimpleXMLRPCRequestHandler.log_message(self, format, *args)
|
||||
|
||||
class ThreadingXMLRPCServer(socketserver.ThreadingMixIn, SimpleXMLRPCServer):
|
||||
"""
|
||||
Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
|
||||
requests via threading. Also makes logging toggleable.
|
||||
"""
|
||||
def __init__(self, addr, log_requests=1):
|
||||
"""
|
||||
Overrides SimpleXMLRPCServer to set option to allow_reuse_address.
|
||||
"""
|
||||
# allow_reuse_address defaults to False in Python 2.4. We set it
|
||||
# to True to allow quick restart on the same port. This is equivalent
|
||||
# to calling setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
|
||||
self.allow_reuse_address = True
|
||||
# Increase request_queue_size to handle issues with many simultaneous
|
||||
# connections in OSX 10.11
|
||||
self.request_queue_size = min(socket.SOMAXCONN, 128)
|
||||
if rosgraph.network.use_ipv6():
|
||||
logger = logging.getLogger('xmlrpc')
|
||||
# The XMLRPC library does not support IPv6 out of the box
|
||||
# We have to monipulate private members and duplicate
|
||||
# code from the constructor.
|
||||
# TODO IPV6: Get this into SimpleXMLRPCServer
|
||||
SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests, bind_and_activate=False)
|
||||
self.address_family = socket.AF_INET6
|
||||
self.socket = socket.socket(self.address_family, self.socket_type)
|
||||
logger.info('binding ipv6 xmlrpc socket to' + str(addr))
|
||||
# TODO: set IPV6_V6ONLY to 0:
|
||||
# self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
||||
self.server_bind()
|
||||
self.server_activate()
|
||||
logger.info('bound to ' + str(self.socket.getsockname()[0:2]))
|
||||
else:
|
||||
SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests)
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
"""
|
||||
override ThreadingMixin, which sends errors to stderr
|
||||
"""
|
||||
if logging and traceback:
|
||||
logger = logging.getLogger('xmlrpc')
|
||||
if logger:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
class ForkingXMLRPCServer(socketserver.ForkingMixIn, SimpleXMLRPCServer):
|
||||
"""
|
||||
Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
|
||||
requests via forking. Also makes logging toggleable.
|
||||
"""
|
||||
def __init__(self, addr, request_handler=SilenceableXMLRPCRequestHandler, log_requests=1):
|
||||
SimpleXMLRPCServer.__init__(self, addr, request_handler, log_requests)
|
||||
|
||||
|
||||
class XmlRpcHandler(object):
|
||||
"""
|
||||
Base handler API for handlers used with XmlRpcNode. Public methods will be
|
||||
exported as XML RPC methods.
|
||||
"""
|
||||
|
||||
def _ready(self, uri):
|
||||
"""
|
||||
callback into handler to inform it of XML-RPC URI
|
||||
"""
|
||||
pass
|
||||
|
||||
def _shutdown(self, reason):
|
||||
"""
|
||||
callback into handler to inform it of shutdown
|
||||
"""
|
||||
pass
|
||||
|
||||
class XmlRpcNode(object):
|
||||
"""
|
||||
Generic XML-RPC node. Handles the additional complexity of binding
|
||||
an XML-RPC server to an arbitrary port.
|
||||
XmlRpcNode is initialized when the uri field has a value.
|
||||
"""
|
||||
|
||||
def __init__(self, port=0, rpc_handler=None, on_run_error=None):
|
||||
"""
|
||||
XML RPC Node constructor
|
||||
:param port: port to use for starting XML-RPC API. Set to 0 or omit to bind to any available port, ``int``
|
||||
:param rpc_handler: XML-RPC API handler for node, `XmlRpcHandler`
|
||||
:param on_run_error: function to invoke if server.run() throws
|
||||
Exception. Server always terminates if run() throws, but this
|
||||
enables cleanup routines to be invoked if server goes down, as
|
||||
well as include additional debugging. ``fn(Exception)``
|
||||
"""
|
||||
super(XmlRpcNode, self).__init__()
|
||||
|
||||
self.handler = rpc_handler
|
||||
self.uri = None # initialize the property now so it can be tested against, will be filled in later
|
||||
self.server = None
|
||||
if port and isstring(port):
|
||||
port = int(port)
|
||||
self.port = port
|
||||
self.is_shutdown = False
|
||||
self.on_run_error = on_run_error
|
||||
|
||||
def shutdown(self, reason):
|
||||
"""
|
||||
Terminate i/o connections for this server.
|
||||
|
||||
:param reason: human-readable debug string, ``str``
|
||||
"""
|
||||
self.is_shutdown = True
|
||||
if self.server:
|
||||
server = self.server
|
||||
handler = self.handler
|
||||
self.handler = self.server = self.port = self.uri = None
|
||||
if handler:
|
||||
handler._shutdown(reason)
|
||||
if server:
|
||||
server.socket.close()
|
||||
server.server_close()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Initiate a thread to run the XML RPC server. Uses thread.start_new_thread.
|
||||
"""
|
||||
_thread.start_new_thread(self.run, ())
|
||||
|
||||
def set_uri(self, uri):
|
||||
"""
|
||||
Sets the XML-RPC URI. Defined as a separate method as a hood
|
||||
for subclasses to bootstrap initialization. Should not be called externally.
|
||||
|
||||
:param uri: XMLRPC URI, ``str``
|
||||
"""
|
||||
self.uri = uri
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self._run()
|
||||
except Exception as e:
|
||||
if self.is_shutdown:
|
||||
pass
|
||||
elif self.on_run_error is not None:
|
||||
self.on_run_error(e)
|
||||
else:
|
||||
raise
|
||||
|
||||
# separated out for easier testing
|
||||
def _run_init(self):
|
||||
logger = logging.getLogger('xmlrpc')
|
||||
try:
|
||||
log_requests = 0
|
||||
port = self.port or 0 #0 = any
|
||||
|
||||
bind_address = rosgraph.network.get_bind_address()
|
||||
logger.info("XML-RPC server binding to %s:%d" % (bind_address, port))
|
||||
|
||||
self.server = ThreadingXMLRPCServer((bind_address, port), log_requests)
|
||||
self.port = self.server.server_address[1] #set the port to whatever server bound to
|
||||
if not self.port:
|
||||
self.port = self.server.socket.getsockname()[1] #Python 2.4
|
||||
|
||||
assert self.port, "Unable to retrieve local address binding"
|
||||
|
||||
# #528: semi-complicated logic for determining XML-RPC URI
|
||||
# - if ROS_IP/ROS_HOSTNAME is set, use that address
|
||||
# - if the hostname returns a non-localhost value, use that
|
||||
# - use whatever rosgraph.network.get_local_address() returns
|
||||
uri = None
|
||||
override = rosgraph.network.get_address_override()
|
||||
if override:
|
||||
uri = 'http://%s:%s/'%(override, self.port)
|
||||
else:
|
||||
try:
|
||||
hostname = socket.gethostname()
|
||||
if hostname and not hostname == 'localhost' and not hostname.startswith('127.') and hostname != '::':
|
||||
uri = 'http://%s:%s/'%(hostname, self.port)
|
||||
except:
|
||||
pass
|
||||
if not uri:
|
||||
uri = 'http://%s:%s/'%(rosgraph.network.get_local_address(), self.port)
|
||||
self.set_uri(uri)
|
||||
|
||||
logger.info("Started XML-RPC server [%s]", self.uri)
|
||||
|
||||
self.server.register_multicall_functions()
|
||||
self.server.register_instance(self.handler)
|
||||
|
||||
except socket.error as e:
|
||||
if e.errno == 98:
|
||||
msg = "ERROR: Unable to start XML-RPC server, port %s is already in use"%self.port
|
||||
else:
|
||||
msg = "ERROR: Unable to start XML-RPC server: %s" % e.strerror
|
||||
logger.error(msg)
|
||||
print(msg)
|
||||
raise #let higher level catch this
|
||||
|
||||
if self.handler is not None:
|
||||
self.handler._ready(self.uri)
|
||||
logger.info("xml rpc node: starting XML-RPC server")
|
||||
|
||||
def _run(self):
|
||||
"""
|
||||
Main processing thread body.
|
||||
:raises: :exc:`socket.error` If server cannot bind
|
||||
|
||||
"""
|
||||
self._run_init()
|
||||
while not self.is_shutdown:
|
||||
try:
|
||||
self.server.serve_forever()
|
||||
except (IOError, select.error) as e:
|
||||
# check for interrupted call, which can occur if we're
|
||||
# embedded in a program using signals. All other
|
||||
# exceptions break _run.
|
||||
if self.is_shutdown:
|
||||
pass
|
||||
elif e.errno != 4:
|
||||
self.is_shutdown = True
|
||||
logging.getLogger('xmlrpc').error("serve forever IOError: %s, %s"%(e.errno, e.strerror))
|
||||
|
||||
0
thirdparty/ros/ros_comm/tools/rosgraph/test/__init__.py
vendored
Normal file
0
thirdparty/ros/ros_comm/tools/rosgraph/test/__init__.py
vendored
Normal file
47
thirdparty/ros/ros_comm/tools/rosgraph/test/test_myargv.py
vendored
Normal file
47
thirdparty/ros/ros_comm/tools/rosgraph/test/test_myargv.py
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2011, 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.
|
||||
|
||||
import sys
|
||||
|
||||
def test_myargv():
|
||||
orig_argv = sys.argv
|
||||
try:
|
||||
from rosgraph import myargv
|
||||
args = myargv()
|
||||
assert args == sys.argv
|
||||
assert ['foo', 'bar', 'baz'] == myargv(['foo','bar', 'baz'])
|
||||
assert ['-foo', 'bar', '-baz'] == myargv(['-foo','bar', '-baz'])
|
||||
|
||||
assert ['foo'] == myargv(['foo','bar:=baz'])
|
||||
assert ['foo'] == myargv(['foo','-bar:=baz'])
|
||||
finally:
|
||||
sys.argv = orig_argv
|
||||
327
thirdparty/ros/ros_comm/tools/rosgraph/test/test_names.py
vendored
Normal file
327
thirdparty/ros/ros_comm/tools/rosgraph/test/test_names.py
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def test_get_ros_namespace():
|
||||
if 'ROS_NAMESPACE' in os.environ:
|
||||
rosns = os.environ['ROS_NAMESPACE']
|
||||
del os.environ['ROS_NAMESPACE']
|
||||
else:
|
||||
rosns = None
|
||||
sysargv = sys.argv
|
||||
|
||||
from rosgraph.names import get_ros_namespace
|
||||
try:
|
||||
sys.argv = []
|
||||
assert '/' == get_ros_namespace()
|
||||
assert '/' == get_ros_namespace(argv=[])
|
||||
assert '/' == get_ros_namespace(env={})
|
||||
assert '/' == get_ros_namespace(env={}, argv=[])
|
||||
|
||||
os.environ['ROS_NAMESPACE'] = 'unresolved'
|
||||
assert '/unresolved/' == get_ros_namespace()
|
||||
assert '/unresolved/' == get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'})
|
||||
sys.argv = ['foo', '__ns:=unresolved_override']
|
||||
assert '/unresolved_override/' == get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'})
|
||||
assert '/override2/' == get_ros_namespace(env={'ROS_NAMESPACE': 'unresolved'}, argv=['foo', '__ns:=override2'])
|
||||
|
||||
sys.argv = []
|
||||
os.environ['ROS_NAMESPACE'] = '/resolved/'
|
||||
assert '/resolved/' == get_ros_namespace()
|
||||
assert '/resolved/' == get_ros_namespace(env={'ROS_NAMESPACE': '/resolved'})
|
||||
|
||||
del os.environ['ROS_NAMESPACE']
|
||||
|
||||
sys.argv = ['foo', '__ns:=unresolved_ns']
|
||||
assert '/unresolved_ns/' == get_ros_namespace()
|
||||
assert '/unresolved_ns2/' == get_ros_namespace(argv=['foo', '__ns:=unresolved_ns2'])
|
||||
sys.argv = ['foo', '__ns:=/resolved_ns/']
|
||||
assert '/resolved_ns/' == get_ros_namespace()
|
||||
assert '/resolved_ns2/' == get_ros_namespace(argv=['foo', '__ns:=resolved_ns2'])
|
||||
finally:
|
||||
sys.argv = sysargv
|
||||
|
||||
# restore
|
||||
if rosns:
|
||||
os.environ['ROS_NAMESPACE'] = rosns
|
||||
|
||||
def test_make_global_ns():
|
||||
from rosgraph.names import make_global_ns
|
||||
|
||||
for n in ['~foo']:
|
||||
try:
|
||||
make_global_ns(n)
|
||||
assert False, "make_global_ns should fail on %s"%n
|
||||
except ValueError: pass
|
||||
|
||||
assert '/foo/' == make_global_ns('foo')
|
||||
assert '/' == make_global_ns('')
|
||||
assert '/foo/' == make_global_ns('/foo')
|
||||
assert '/foo/' == make_global_ns('/foo/')
|
||||
assert '/foo/bar/' == make_global_ns('/foo/bar')
|
||||
assert '/foo/bar/' == make_global_ns('/foo/bar/')
|
||||
|
||||
def test_make_caller_id():
|
||||
from rosgraph.names import make_caller_id
|
||||
if 'ROS_NAMESPACE' is os.environ:
|
||||
rosns = os.environ['ROS_NAMESPACE']
|
||||
del os.environ['ROS_NAMESPACE']
|
||||
else:
|
||||
rosns = None
|
||||
|
||||
for n in ['~name']:
|
||||
try:
|
||||
make_caller_id('~name') # illegal
|
||||
assert False, "make_caller_id should fail on %s"%n
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
assert '/node/' == make_caller_id('node')
|
||||
assert '/bar/node/' == make_caller_id('bar/node')
|
||||
assert '/bar/node/' == make_caller_id('/bar/node')
|
||||
|
||||
os.environ['ROS_NAMESPACE'] = '/test/'
|
||||
assert '/test/node/' == make_caller_id('node')
|
||||
assert '/test/bar/node/' == make_caller_id('bar/node')
|
||||
assert '/bar/node/' == make_caller_id('/bar/node')
|
||||
|
||||
# restore
|
||||
if rosns:
|
||||
os.environ['ROS_NAMESPACE'] = rosns
|
||||
|
||||
def test_is_global():
|
||||
from rosgraph.names import is_global
|
||||
try:
|
||||
is_global(None)
|
||||
assert False, "is_global should raise exception on invalid param"
|
||||
except: pass
|
||||
tests = ['/', '/global', '/global2']
|
||||
for t in tests:
|
||||
assert is_global(t)
|
||||
fails = ['', 'not_global', 'not/global']
|
||||
for t in fails:
|
||||
assert not is_global(t)
|
||||
|
||||
def test_is_private():
|
||||
from rosgraph.names import is_private
|
||||
try:
|
||||
is_private(None)
|
||||
assert False, "is_private should raise exception on invalid param"
|
||||
except: pass
|
||||
tests = ['~name', '~name/sub']
|
||||
for t in tests:
|
||||
assert is_private(t)
|
||||
fails = ['', 'not_private', 'not/private', 'not/~private', '/not/~private']
|
||||
for t in fails:
|
||||
assert not is_private(t)
|
||||
|
||||
def test_namespace():
|
||||
from rosgraph.names import namespace
|
||||
try:
|
||||
namespace(1)
|
||||
assert False, "1"
|
||||
except TypeError: pass
|
||||
try:
|
||||
namespace(None)
|
||||
assert False, "None"
|
||||
except ValueError: pass
|
||||
assert '/'== namespace('')
|
||||
assert '/'== namespace('/')
|
||||
assert '/'== namespace('/foo')
|
||||
assert '/'== namespace('/foo/')
|
||||
assert '/foo/'== namespace('/foo/bar')
|
||||
assert '/foo/'== namespace('/foo/bar/')
|
||||
assert '/foo/bar/'== namespace('/foo/bar/baz')
|
||||
assert '/foo/bar/'== namespace('/foo/bar/baz/')
|
||||
|
||||
# unicode tests
|
||||
assert u'/'== namespace(u'')
|
||||
assert u'/'== namespace(u'/')
|
||||
assert u'/foo/bar/'== namespace(u'/foo/bar/baz/')
|
||||
|
||||
def test_nsjoin():
|
||||
from rosgraph.names import ns_join
|
||||
|
||||
# private and global names cannot be joined
|
||||
assert '~name' == ns_join('/foo', '~name')
|
||||
assert '/name' == ns_join('/foo', '/name')
|
||||
assert '~name' == ns_join('~', '~name')
|
||||
assert '/name' == ns_join('/', '/name')
|
||||
|
||||
# ns can be '~' or '/'
|
||||
assert '~name' == ns_join('~', 'name')
|
||||
assert '/name' == ns_join('/', 'name')
|
||||
|
||||
assert '/ns/name' == ns_join('/ns', 'name')
|
||||
assert '/ns/name' == ns_join('/ns/', 'name')
|
||||
assert '/ns/ns2/name' == ns_join('/ns', 'ns2/name')
|
||||
assert '/ns/ns2/name' == ns_join('/ns/', 'ns2/name')
|
||||
|
||||
# allow ns to be empty
|
||||
assert 'name' == ns_join('', 'name')
|
||||
|
||||
|
||||
def test_load_mappings():
|
||||
from rosgraph.names import load_mappings
|
||||
assert {} == load_mappings([])
|
||||
assert {} == load_mappings(['foo'])
|
||||
assert {} == load_mappings([':='])
|
||||
assert {} == load_mappings([':=:='])
|
||||
assert {} == load_mappings(['f:='])
|
||||
assert {} == load_mappings([':=b'])
|
||||
assert {} == load_mappings(['foo:=bar:=baz'])
|
||||
# should ignore node param assignments
|
||||
assert {} == load_mappings(['_foo:=bar'])
|
||||
|
||||
assert {'foo': 'bar'} == load_mappings(['foo:=bar'])
|
||||
# should allow double-underscore names
|
||||
assert {'__foo': 'bar'} == load_mappings(['__foo:=bar'])
|
||||
assert {'foo': 'bar'} == load_mappings(['./f', '-x', '--blah', 'foo:=bar'])
|
||||
assert {'a': '1', 'b': '2', 'c': '3'} == load_mappings(['c:=3', 'c:=', ':=3', 'a:=1', 'b:=2'])
|
||||
|
||||
def test_is_legal_name():
|
||||
from rosgraph.names import is_legal_name
|
||||
failures = [None,
|
||||
'foo++', 'foo-bar', '#foo',
|
||||
'hello\n', '\t', ' name', 'name ',
|
||||
'f//b',
|
||||
'1name', 'foo\\']
|
||||
for f in failures:
|
||||
assert not is_legal_name(f), f
|
||||
tests = ['',
|
||||
'f', 'f1', 'f_', 'f/', 'foo', 'foo_bar', 'foo/bar', 'foo/bar/baz',
|
||||
'~f', '~a/b/c',
|
||||
'~/f',
|
||||
'/a/b/c/d', '/']
|
||||
for t in tests:
|
||||
assert is_legal_name(t), "[%s]"%t
|
||||
|
||||
def test_is_legal_base_name():
|
||||
from rosgraph.names import is_legal_base_name
|
||||
failures = [None, '', 'hello\n', '\t', 'foo++', 'foo-bar', '#foo',
|
||||
'f/', 'foo/bar', '/', '/a',
|
||||
'f//b',
|
||||
'~f', '~a/b/c',
|
||||
' name', 'name ',
|
||||
'1name', 'foo\\']
|
||||
for f in failures:
|
||||
assert not is_legal_base_name(f), f
|
||||
tests = ['f', 'f1', 'f_', 'foo', 'foo_bar']
|
||||
for t in tests:
|
||||
assert is_legal_base_name(t), "[%s]"%t
|
||||
|
||||
def test_resolve_name():
|
||||
from rosgraph.names import resolve_name
|
||||
# TODO: test with remappings
|
||||
tests = [
|
||||
('', '/', '/'),
|
||||
('', '/node', '/'),
|
||||
('', '/ns1/node', '/ns1/'),
|
||||
|
||||
('foo', '', '/foo'),
|
||||
('foo/', '', '/foo'),
|
||||
('/foo', '', '/foo'),
|
||||
('/foo/', '', '/foo'),
|
||||
('/foo', '/', '/foo'),
|
||||
('/foo/', '/', '/foo'),
|
||||
('/foo', '/bar', '/foo'),
|
||||
('/foo/', '/bar', '/foo'),
|
||||
|
||||
('foo', '/ns1/ns2', '/ns1/foo'),
|
||||
('foo', '/ns1/ns2/', '/ns1/foo'),
|
||||
('foo', '/ns1/ns2/ns3/', '/ns1/ns2/foo'),
|
||||
('foo/', '/ns1/ns2', '/ns1/foo'),
|
||||
('/foo', '/ns1/ns2', '/foo'),
|
||||
('foo/bar', '/ns1/ns2', '/ns1/foo/bar'),
|
||||
('foo//bar', '/ns1/ns2', '/ns1/foo/bar'),
|
||||
('foo/bar', '/ns1/ns2/ns3', '/ns1/ns2/foo/bar'),
|
||||
('foo//bar//', '/ns1/ns2/ns3', '/ns1/ns2/foo/bar'),
|
||||
|
||||
('~foo', '/', '/foo'),
|
||||
('~foo', '/node', '/node/foo'),
|
||||
('~foo', '/ns1/ns2', '/ns1/ns2/foo'),
|
||||
('~foo/', '/ns1/ns2', '/ns1/ns2/foo'),
|
||||
('~foo/bar', '/ns1/ns2', '/ns1/ns2/foo/bar'),
|
||||
|
||||
# #3044
|
||||
('~/foo', '/', '/foo'),
|
||||
('~/foo', '/node', '/node/foo'),
|
||||
('~/foo', '/ns1/ns2', '/ns1/ns2/foo'),
|
||||
('~/foo/', '/ns1/ns2', '/ns1/ns2/foo'),
|
||||
('~/foo/bar', '/ns1/ns2', '/ns1/ns2/foo/bar'),
|
||||
|
||||
]
|
||||
for name, node_name, v in tests:
|
||||
assert v == resolve_name(name, node_name)
|
||||
|
||||
def test_anonymous_name():
|
||||
from rosgraph.names import anonymous_name, is_legal_name
|
||||
val = anonymous_name('foo')
|
||||
assert 'foo' in val
|
||||
assert 'foo' != val
|
||||
assert val != anonymous_name('foo')
|
||||
assert not '/' in val
|
||||
assert is_legal_name(val)
|
||||
|
||||
|
||||
def test_script_resolve_name():
|
||||
from rosgraph.names import script_resolve_name, get_ros_namespace, ns_join
|
||||
|
||||
assert '/global' == script_resolve_name('/myscript', '/global')
|
||||
val = script_resolve_name('/myscript', '')
|
||||
assert get_ros_namespace() == val, val
|
||||
val = script_resolve_name('/myscript', 'foo')
|
||||
assert ns_join(get_ros_namespace(), 'foo') == val, val
|
||||
assert '/myscript/private' == script_resolve_name('/myscript', '~private')
|
||||
|
||||
def test_canonicalize_name():
|
||||
from rosgraph.names import canonicalize_name
|
||||
tests = [
|
||||
('', ''),
|
||||
('/', '/'),
|
||||
('foo', 'foo'),
|
||||
('/foo', '/foo'),
|
||||
('/foo/', '/foo'),
|
||||
('/foo/bar', '/foo/bar'),
|
||||
('/foo/bar/', '/foo/bar'),
|
||||
('/foo/bar//', '/foo/bar'),
|
||||
('/foo//bar', '/foo/bar'),
|
||||
('//foo/bar', '/foo/bar'),
|
||||
('foo/bar', 'foo/bar'),
|
||||
('foo//bar', 'foo/bar'),
|
||||
('foo/bar/', 'foo/bar'),
|
||||
('/foo/bar', '/foo/bar'),
|
||||
]
|
||||
for t, v in tests:
|
||||
assert v == canonicalize_name(t)
|
||||
250
thirdparty/ros/ros_comm/tools/rosgraph/test/test_network.py
vendored
Normal file
250
thirdparty/ros/ros_comm/tools/rosgraph/test/test_network.py
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import unittest
|
||||
|
||||
class MockSock(object):
|
||||
def __init__(self, data=''):
|
||||
self.data = data
|
||||
def recv(self):
|
||||
d = self.data
|
||||
self.data = ''
|
||||
return d
|
||||
def sendall(self, d):
|
||||
self.data = self.data + d
|
||||
def send(self, d):
|
||||
self.data = self.data + d
|
||||
return len(d)
|
||||
|
||||
|
||||
class NetworkTest(unittest.TestCase):
|
||||
|
||||
def test_encode_ros_handshake_header(self):
|
||||
from rosgraph.network import encode_ros_handshake_header
|
||||
d = {}
|
||||
assert struct.pack('<I', 0) == encode_ros_handshake_header(d)
|
||||
s = b"a=b"
|
||||
d['a'] = 'b'
|
||||
encoded = struct.pack('<I', len(s))+s
|
||||
assert struct.pack('<I', len(encoded))+encoded == \
|
||||
encode_ros_handshake_header({'a': 'b'})
|
||||
d['c'] = 'd'
|
||||
s = b"c=d"
|
||||
encoded = encoded+struct.pack('<I', len(s))+s
|
||||
assert struct.pack('<I', len(encoded))+encoded == \
|
||||
encode_ros_handshake_header(d)
|
||||
d['rawtype'] = '#line 1\nline 2\nline 3\nline\t4\r\r\n'
|
||||
s = b"rawtype=#line 1\nline 2\nline 3\nline\t4\r\r\n"
|
||||
encoded = encoded+struct.pack('<I', len(s))+s
|
||||
assert struct.pack('<I', len(encoded))+encoded == \
|
||||
encode_ros_handshake_header(d)
|
||||
if sys.version_info > (3, 0):
|
||||
assert \
|
||||
encode_ros_handshake_header(
|
||||
{'a': 'b', 'c': 'd', 'e': 'f'}) == \
|
||||
encode_ros_handshake_header(
|
||||
{b'a': 'b', 'c': b'd', b'e': b'f'})
|
||||
else:
|
||||
assert \
|
||||
encode_ros_handshake_header(
|
||||
{'a': 'b', 'c': 'd', 'e': 'f'}) == \
|
||||
encode_ros_handshake_header(
|
||||
{u'a': 'b', 'c': u'd', u'e': u'f'})
|
||||
|
||||
|
||||
def test_decode_ros_handshake_header(self):
|
||||
from rosgraph.network import decode_ros_handshake_header, ROSHandshakeException
|
||||
|
||||
invalids = [b"field1",b"",]
|
||||
# prepend field length
|
||||
invalids = [(struct.pack('<I', len(s)) + s) for s in invalids]
|
||||
# prepend message length
|
||||
invalids = [(struct.pack('<I', len(s)) + s) for s in invalids]
|
||||
|
||||
# invalid message length prefix
|
||||
valid = b"a=b"
|
||||
valid = struct.pack('<I', len(valid)) + valid
|
||||
invalids.append(struct.pack('<I', 123)+valid)
|
||||
# invalid field length prefix
|
||||
invalid = struct.pack('<I', 123) + b'a=b'
|
||||
invalids.append(struct.pack("<I", len(invalid)) + invalid)
|
||||
|
||||
for i in invalids:
|
||||
try:
|
||||
decode_ros_handshake_header(i)
|
||||
assert False, "should have failed: %s"%i
|
||||
except ROSHandshakeException: pass
|
||||
|
||||
assert {} == decode_ros_handshake_header(struct.pack('<I', 0))
|
||||
# single-field tests
|
||||
tests = [
|
||||
(b"a=b", {'a': 'b'}),
|
||||
# whitespace in keys is ignored
|
||||
(b" a =b", {'a': 'b'}),
|
||||
(b'newlines=\n\n\n\n', {'newlines': '\n\n\n\n'}),
|
||||
(b'equals=foo=bar=car', {'equals': 'foo=bar=car'}),
|
||||
(b"spaces=one two three four",{'spaces': 'one two three four'}),
|
||||
]
|
||||
for s, d in tests:
|
||||
# add in length fields
|
||||
s = struct.pack('<I', len(s)+4) + struct.pack('<I', len(s)) + s
|
||||
assert d == decode_ros_handshake_header(s)
|
||||
|
||||
# multi-field tests
|
||||
tests = [ {'a': 'b', 'c': 'd'},
|
||||
{'spaces': ' ', 'tabs': '\t\t\t\t', 'equals': '====='},
|
||||
]
|
||||
for t in tests:
|
||||
s = b''
|
||||
for k, v in t.items():
|
||||
f = "%s=%s"%(k, v)
|
||||
f = f.encode()
|
||||
s += struct.pack('<I', len(f)) + f
|
||||
s = struct.pack('<I', len(s)) + s
|
||||
assert t == decode_ros_handshake_header(s)
|
||||
# make sure that decode ignores extra past header len
|
||||
assert t == decode_ros_handshake_header(s+s)
|
||||
|
||||
def test_parse_http_host_and_port(self):
|
||||
from rosgraph.network import parse_http_host_and_port
|
||||
invalid = ['', 'http://', 'http://localhost:bar', None]
|
||||
for t in invalid:
|
||||
try:
|
||||
parse_http_host_and_port(t)
|
||||
assert False, "should have failed: %s"%t
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
assert ('localhost', 80) == parse_http_host_and_port('http://localhost')
|
||||
assert ('localhost', 1234) == parse_http_host_and_port('http://localhost:1234')
|
||||
assert ('localhost', 1) == parse_http_host_and_port('http://localhost:1')
|
||||
assert ('willowgarage.com', 1) == parse_http_host_and_port('http://willowgarage.com:1')
|
||||
|
||||
def test_get_local_address(self):
|
||||
# mostly a tripwire test
|
||||
from rosgraph.network import get_local_address
|
||||
a = get_local_address()
|
||||
assert a
|
||||
|
||||
# now test address override
|
||||
os.environ['ROS_IP'] = 'bar'
|
||||
assert 'bar' == get_local_address()
|
||||
os.environ['ROS_HOSTNAME'] = 'foo'
|
||||
assert 'foo' == get_local_address()
|
||||
|
||||
try:
|
||||
real_argv = sys.argv[:]
|
||||
sys.argv = real_argv[:] + ['__ip:=1.2.3.4']
|
||||
assert '1.2.3.4' == get_local_address()
|
||||
sys.argv = real_argv[:] + ['__hostname:=bar']
|
||||
assert 'bar' == get_local_address()
|
||||
finally:
|
||||
sys.argv = real_argv
|
||||
|
||||
def test_get_local_addresses(self):
|
||||
# mostly a tripwire test
|
||||
from rosgraph.network import get_local_addresses
|
||||
addrs = get_local_addresses()
|
||||
assert type(addrs) == list
|
||||
assert len(addrs)
|
||||
|
||||
# should be unaffected
|
||||
os.environ['ROS_IP'] = 'bar'
|
||||
assert addrs == get_local_addresses()
|
||||
os.environ['ROS_HOSTNAME'] = 'foo'
|
||||
assert addrs == get_local_addresses()
|
||||
|
||||
def test_get_bind_address(self):
|
||||
from rosgraph.network import get_bind_address
|
||||
assert '0.0.0.0' == get_bind_address('foo')
|
||||
assert '127.0.0.1' == get_bind_address('localhost')
|
||||
assert '127.0.1.1' == get_bind_address('127.0.1.1')
|
||||
|
||||
# now test address override
|
||||
os.environ['ROS_IP'] = 'bar'
|
||||
assert '0.0.0.0' == get_bind_address()
|
||||
assert '0.0.0.0' == get_bind_address('foo')
|
||||
os.environ['ROS_IP'] = 'localhost'
|
||||
assert '127.0.0.1' == get_bind_address()
|
||||
assert '0.0.0.0' == get_bind_address('foo')
|
||||
os.environ['ROS_HOSTNAME'] = 'bar'
|
||||
assert '0.0.0.0' == get_bind_address()
|
||||
assert '0.0.0.0' == get_bind_address('foo')
|
||||
os.environ['ROS_HOSTNAME'] = 'localhost'
|
||||
assert '127.0.0.1' == get_bind_address()
|
||||
assert '0.0.0.0' == get_bind_address('foo')
|
||||
|
||||
def test_get_host_name(self):
|
||||
from rosgraph.network import get_host_name
|
||||
|
||||
os.environ['ROS_IP'] = 'foo'
|
||||
assert 'foo' == get_host_name()
|
||||
os.environ['ROS_HOSTNAME'] = 'bar'
|
||||
assert 'bar' == get_host_name()
|
||||
|
||||
try:
|
||||
real_argv = sys.argv[:]
|
||||
sys.argv = real_argv[:] + ['__ip:=1.2.3.4']
|
||||
assert '1.2.3.4' == get_host_name()
|
||||
sys.argv = real_argv[:] + ['__hostname:=baz']
|
||||
assert 'baz' == get_host_name()
|
||||
finally:
|
||||
sys.argv = real_argv
|
||||
|
||||
def test_create_local_xmlrpc_uri(self):
|
||||
from rosgraph.network import parse_http_host_and_port, create_local_xmlrpc_uri
|
||||
os.environ['ROS_HOSTNAME'] = 'localhost'
|
||||
assert ('localhost', 1234) == parse_http_host_and_port(create_local_xmlrpc_uri(1234))
|
||||
|
||||
def setUp(self):
|
||||
self._ros_hostname = self._ros_ip = None
|
||||
if 'ROS_HOSTNAME' in os.environ:
|
||||
self._ros_hostname = os.environ['ROS_HOSTNAME']
|
||||
del os.environ['ROS_HOSTNAME']
|
||||
if 'ROS_IP' in os.environ:
|
||||
self._ros_ip = os.environ['ROS_IP']
|
||||
del os.environ['ROS_IP']
|
||||
|
||||
def tearDown(self):
|
||||
if 'ROS_HOSTNAME' in os.environ:
|
||||
del os.environ['ROS_HOSTNAME']
|
||||
if 'ROS_IP' in os.environ:
|
||||
del os.environ['ROS_IP']
|
||||
if self._ros_hostname:
|
||||
os.environ['ROS_HOSTNAME'] = self._ros_hostname
|
||||
if self._ros_ip:
|
||||
os.environ['ROS_IP'] = self._ros_ip
|
||||
|
||||
77
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosenv.py
vendored
Normal file
77
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosenv.py
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2011, 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def test_vars():
|
||||
import rosgraph.rosenv
|
||||
assert 'ROS_MASTER_URI' == rosgraph.rosenv.ROS_MASTER_URI
|
||||
assert rosgraph.rosenv.ROS_IP == 'ROS_IP'
|
||||
assert rosgraph.rosenv.ROS_HOSTNAME == 'ROS_HOSTNAME'
|
||||
assert rosgraph.rosenv.ROS_NAMESPACE == 'ROS_NAMESPACE'
|
||||
|
||||
def test_get_master_uri():
|
||||
from rosgraph.rosenv import get_master_uri
|
||||
val = get_master_uri()
|
||||
if 'ROS_MASTER_URI' in os.environ:
|
||||
assert val == os.environ['ROS_MASTER_URI']
|
||||
|
||||
# environment override
|
||||
val = get_master_uri(env=dict(ROS_MASTER_URI='foo'))
|
||||
assert val == 'foo'
|
||||
|
||||
# argv override precedence, first arg wins
|
||||
val = get_master_uri(env=dict(ROS_MASTER_URI='foo'), argv=['__master:=bar', '__master:=bar2'])
|
||||
assert val == 'bar'
|
||||
|
||||
# empty env
|
||||
assert None == get_master_uri(env={})
|
||||
|
||||
# invalid argv
|
||||
try:
|
||||
val = get_master_uri(argv=['__master:='])
|
||||
assert False, "should have failed"
|
||||
except ValueError:
|
||||
pass
|
||||
# invalid argv
|
||||
try:
|
||||
val = get_master_uri(argv=['__master:=foo:=bar'])
|
||||
assert False, "should have failed"
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
63
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosgraph_command_offline.py
vendored
Normal file
63
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosgraph_command_offline.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import time
|
||||
|
||||
from subprocess import Popen, PIPE, check_call, call
|
||||
|
||||
class TestRosgraphOffline(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
## test that the rosmsg command works
|
||||
def test_cmd_help(self):
|
||||
cmd = 'rosgraph'
|
||||
output = Popen([cmd, '-h'], stdout=PIPE).communicate()[0]
|
||||
self.assert_('Usage' in output.decode())
|
||||
|
||||
def test_offline(self):
|
||||
cmd = 'rosgraph'
|
||||
|
||||
# point at a different 'master'
|
||||
env = os.environ.copy()
|
||||
env['ROS_MASTER_URI'] = 'http://localhost:11312'
|
||||
kwds = { 'env': env, 'stdout': PIPE, 'stderr': PIPE}
|
||||
|
||||
msg = "ERROR: Unable to communicate with master!\n"
|
||||
|
||||
output = Popen([cmd], **kwds).communicate()
|
||||
self.assertEquals(msg, output[1].decode())
|
||||
398
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosgraph_masterapi_offline.py
vendored
Normal file
398
thirdparty/ros/ros_comm/tools/rosgraph/test/test_rosgraph_masterapi_offline.py
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import rosgraph.masterapi
|
||||
|
||||
_ID = '/caller_id'
|
||||
_MASTER_URI = 'http://localhost:12345'
|
||||
|
||||
class MasterMock(object):
|
||||
"""
|
||||
Mock for testing Master without using actual master
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.call = None
|
||||
self.return_val = None
|
||||
|
||||
def getMasterUri(self, caller_id):
|
||||
self.call = ('getMasterUri', caller_id)
|
||||
return self.return_val
|
||||
|
||||
def getPid(self, caller_id):
|
||||
self.call = ('getPid', caller_id)
|
||||
return self.return_val
|
||||
|
||||
def getUri(self, caller_id):
|
||||
self.call = ('getUri', caller_id)
|
||||
return self.return_val
|
||||
|
||||
def registerService(self, caller_id, service, service_api, caller_api):
|
||||
self.call = ('registerService', caller_id, service, service_api, caller_api)
|
||||
return self.return_val
|
||||
|
||||
def lookupService(self, caller_id, service):
|
||||
self.call = ('lookupService', caller_id, service)
|
||||
return self.return_val
|
||||
|
||||
def unregisterService(self, caller_id, service, service_api):
|
||||
self.call = ('unregisterService', caller_id, service, service_api)
|
||||
return self.return_val
|
||||
|
||||
def registerSubscriber(self, caller_id, topic, topic_type, caller_api):
|
||||
self.call = ('registerSubscriber', caller_id, topic, topic_type, caller_api)
|
||||
return self.return_val
|
||||
|
||||
def unregisterSubscriber(self, caller_id, topic, caller_api):
|
||||
self.call = ('unregisterSubscriber', caller_id, topic, caller_api)
|
||||
return self.return_val
|
||||
|
||||
def registerPublisher(self, caller_id, topic, topic_type, caller_api):
|
||||
self.call = ('registerPublisher', caller_id, topic, topic_type, caller_api)
|
||||
return self.return_val
|
||||
|
||||
def unregisterPublisher(self, caller_id, topic, caller_api):
|
||||
self.call = ('unregisterPublisher', caller_id, topic, caller_api)
|
||||
return self.return_val
|
||||
|
||||
def lookupNode(self, caller_id, node_name):
|
||||
self.call = ('lookupNode', caller_id, node_name)
|
||||
return self.return_val
|
||||
|
||||
def getPublishedTopics(self, caller_id, subgraph):
|
||||
self.call = ('getPublishedTopics', caller_id, subgraph)
|
||||
return self.return_val
|
||||
|
||||
def getTopicTypes(self, caller_id):
|
||||
self.call = ('getTopicTypes', caller_id)
|
||||
return self.return_val
|
||||
|
||||
def getSystemState(self, caller_id):
|
||||
self.call = ('getSystemState', caller_id)
|
||||
return self.return_val
|
||||
|
||||
def getParam(self, caller_id, p):
|
||||
self.call = ('getParam', caller_id, p)
|
||||
return self.return_val
|
||||
|
||||
def hasParam(self, caller_id, p):
|
||||
self.call = ('hasParam', caller_id, p)
|
||||
return self.return_val
|
||||
|
||||
def deleteParam(self, caller_id, p):
|
||||
self.call = ('deleteParam', caller_id, p)
|
||||
return self.return_val
|
||||
|
||||
def searchParam(self, caller_id, p):
|
||||
self.call = ('searchParam', caller_id, p)
|
||||
return self.return_val
|
||||
|
||||
def setParam(self, caller_id, p, v):
|
||||
self.call = ('setParam', caller_id, p, v)
|
||||
return self.return_val
|
||||
|
||||
def subscribeParam(self, caller_id, api, p):
|
||||
self.call = ('subscribeParam', caller_id, api, p)
|
||||
return self.return_val
|
||||
|
||||
def unsubscribeParam(self, caller_id, api, p):
|
||||
self.call = ('unsubscribeParam', caller_id, api, p)
|
||||
return self.return_val
|
||||
|
||||
def getParamNames(self, caller_id):
|
||||
self.call = ('getParamNames', caller_id)
|
||||
return self.return_val
|
||||
|
||||
class MasterApiOfflineTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.m = rosgraph.masterapi.Master(_ID, master_uri = _MASTER_URI)
|
||||
# replace xmlrpclib server proxy with mock
|
||||
self.m.handle = MasterMock()
|
||||
|
||||
def throw_failure(self, attr, args, ret_val):
|
||||
self.m.handle.return_val = ret_val
|
||||
f = getattr(self.m, attr)
|
||||
try:
|
||||
f(*args)
|
||||
self.fail("[%s] should have thrown Failure with args [%s], ret_val [%s]"%(attr, str(args), str(ret_val)))
|
||||
except rosgraph.masterapi.Failure:
|
||||
pass
|
||||
|
||||
def throw_error(self, attr, args, ret_val):
|
||||
self.m.handle.return_val = ret_val
|
||||
f = getattr(self.m, attr)
|
||||
try:
|
||||
f(*args)
|
||||
self.fail("[%s] should have thrown Error with args [%s], ret_val [%s]"%(attr, str(args), str(ret_val)))
|
||||
except rosgraph.masterapi.Error:
|
||||
pass
|
||||
|
||||
def test_Master(self):
|
||||
# test constructor args
|
||||
m = rosgraph.masterapi.Master(_ID, master_uri = 'http://localhost:12345')
|
||||
self.assertEquals(_ID, m.caller_id)
|
||||
self.assertEquals(_MASTER_URI, m.master_uri)
|
||||
|
||||
reset_uri = False
|
||||
if 'ROS_MASTER_URI' not in os.environ:
|
||||
os.environ['ROS_MASTER_URI'] = 'http://localhost:21311'
|
||||
|
||||
try:
|
||||
m = rosgraph.masterapi.Master(_ID)
|
||||
self.assertEquals(os.environ['ROS_MASTER_URI'], m.master_uri)
|
||||
|
||||
id = '/some/other/id'
|
||||
m = rosgraph.masterapi.Master(id)
|
||||
self.assertEquals(id, m.caller_id)
|
||||
finally:
|
||||
if reset_uri:
|
||||
del os.environ['ROS_MASTER_URI']
|
||||
|
||||
def test_getPid(self):
|
||||
h = self.m.handle
|
||||
r = 1235
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getPid())
|
||||
self.assertEquals(('getPid',_ID), h.call)
|
||||
self.throw_failure('getPid', (), (0, '', r))
|
||||
self.throw_error('getPid', (), (-1, '', r))
|
||||
|
||||
def test_getPid(self):
|
||||
h = self.m.handle
|
||||
r = 'http://foo:1234'
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getUri())
|
||||
self.assertEquals(('getUri',_ID), h.call)
|
||||
self.throw_failure('getUri', (), (0, '', r))
|
||||
self.throw_error('getUri', (), (-1, '', r))
|
||||
|
||||
def test_lookupService(self):
|
||||
h = self.m.handle
|
||||
r = 'rosrpc://localhost:12345'
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.lookupService('/bar/service'))
|
||||
self.assertEquals(('lookupService',_ID, '/bar/service'), h.call)
|
||||
self.throw_failure('lookupService', ('/bar/service',), (0, '', r))
|
||||
self.throw_error('lookupService', ('/bar/service',), (-1, '', r))
|
||||
|
||||
def test_registerService(self):
|
||||
h = self.m.handle
|
||||
r = 11
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.registerService('/bar/service', 'rosrpc://localhost:9812', 'http://localhost:893'))
|
||||
self.assertEquals(('registerService',_ID, '/bar/service', 'rosrpc://localhost:9812', 'http://localhost:893'), h.call)
|
||||
args = ('/bar/service', 'rosrpc://localhost:9812', 'http://localhost:893')
|
||||
self.throw_failure('registerService', args, (0, '', r))
|
||||
self.throw_error('registerService', args, (-1, '', r))
|
||||
|
||||
def test_unregisterService(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.unregisterService('/bar/service', 'rosrpc://localhost:9812'))
|
||||
self.assertEquals(('unregisterService',_ID, '/bar/service', 'rosrpc://localhost:9812'), h.call)
|
||||
args = ('/bar/service', 'rosrpc://localhost:9812')
|
||||
self.throw_failure('unregisterService', args, (0, '', r))
|
||||
self.throw_error('unregisterService', args, (-1, '', r))
|
||||
|
||||
def test_registerSubscriber(self):
|
||||
h = self.m.handle
|
||||
r = ['http://localhost:1234', 'http://localhost:98127']
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.registerSubscriber('/foo/node', 'std_msgs/String', 'http://localhost:9812'))
|
||||
self.assertEquals(('registerSubscriber',_ID, '/foo/node', 'std_msgs/String', 'http://localhost:9812'), h.call)
|
||||
args = ('/foo/node', 'std_msgs/String', 'http://localhost:9812')
|
||||
self.throw_failure('registerSubscriber', args, (0, '', r))
|
||||
self.throw_error('registerSubscriber', args, (-1, '', r))
|
||||
|
||||
def test_unregisterSubscriber(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.unregisterSubscriber('/foo/node', 'http://localhost:9812'))
|
||||
self.assertEquals(('unregisterSubscriber',_ID, '/foo/node', 'http://localhost:9812'), h.call)
|
||||
args = ('/foo/node', 'http://localhost:9812')
|
||||
self.throw_failure('unregisterSubscriber', args, (0, '', r))
|
||||
self.throw_error('unregisterSubscriber', args, (-1, '', r))
|
||||
|
||||
def test_registerPublisher(self):
|
||||
h = self.m.handle
|
||||
r = 5
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.registerPublisher('/foo/node', 'std_msgs/String', 'http://localhost:9812'))
|
||||
self.assertEquals(('registerPublisher',_ID, '/foo/node', 'std_msgs/String', 'http://localhost:9812'), h.call)
|
||||
args = ('/foo/node', 'std_msgs/String', 'http://localhost:9812')
|
||||
self.throw_failure('registerPublisher', args, (0, '', r))
|
||||
self.throw_error('registerPublisher', args, (-1, '', r))
|
||||
|
||||
def test_unregisterPublisher(self):
|
||||
h = self.m.handle
|
||||
r = 10
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.unregisterPublisher('/foo/node', 'http://localhost:9812'))
|
||||
self.assertEquals(('unregisterPublisher',_ID, '/foo/node', 'http://localhost:9812'), h.call)
|
||||
args = ('/foo/node', 'http://localhost:9812')
|
||||
self.throw_failure('unregisterPublisher', args, (0, '', r))
|
||||
self.throw_error('unregisterPublisher', args, (-1, '', r))
|
||||
|
||||
def test_lookupNode(self):
|
||||
h = self.m.handle
|
||||
r = 'http://localhost:123'
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.lookupNode('/foo/node'))
|
||||
self.assertEquals(('lookupNode',_ID, '/foo/node'), h.call)
|
||||
args = ('/foo/node',)
|
||||
self.throw_failure('lookupNode', args, (0, '', r))
|
||||
self.throw_error('lookupNode', args, (-1, '', r))
|
||||
|
||||
def test_getPublishedTopics(self):
|
||||
h = self.m.handle
|
||||
r = [ ['foo', 'bar'], ['baz', 'blah'] ]
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getPublishedTopics('/foo'))
|
||||
self.assertEquals(('getPublishedTopics',_ID, '/foo'), h.call)
|
||||
args = ('/baz',)
|
||||
self.throw_failure('getPublishedTopics', args, (0, '', r))
|
||||
self.throw_error('getPublishedTopics', args, (-1, '', r))
|
||||
|
||||
def test_getTopicTypes(self):
|
||||
h = self.m.handle
|
||||
r = [ ['/foo', 'std_msgs/String'], ['/baz', 'std_msgs/Int32'] ]
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getTopicTypes())
|
||||
self.assertEquals(('getTopicTypes',_ID), h.call)
|
||||
self.throw_failure('getTopicTypes', (), (0, '', r))
|
||||
self.throw_error('getTopicTypes', (), (-1, '', r))
|
||||
|
||||
|
||||
def test_getSystemState(self):
|
||||
h = self.m.handle
|
||||
r = [ [], [], [] ]
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getSystemState())
|
||||
self.assertEquals(('getSystemState', _ID), h.call)
|
||||
self.throw_failure('getSystemState', (), (0, '', r))
|
||||
self.throw_error('getSystemState', (), (-1, '', r))
|
||||
|
||||
################################################################################
|
||||
# PARAM SERVER API TESTS
|
||||
|
||||
def test_getParam(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
p = '/test_param'
|
||||
self.assertEquals(r, self.m.getParam(p))
|
||||
self.assertEquals(('getParam', _ID, p), h.call)
|
||||
args = (p,)
|
||||
self.throw_failure('getParam', args, (0, '', r))
|
||||
self.throw_error('getParam', args, (-1, '', r))
|
||||
|
||||
def test_getParamNames(self):
|
||||
h = self.m.handle
|
||||
r = [ '/foo' ]
|
||||
h.return_val = (1, '', r)
|
||||
self.assertEquals(r, self.m.getParamNames())
|
||||
self.assertEquals(('getParamNames', _ID), h.call)
|
||||
self.throw_failure('getParamNames', (), (0, '', r))
|
||||
self.throw_error('getParamNames', (), (-1, '', r))
|
||||
|
||||
def test_hasParam(self):
|
||||
h = self.m.handle
|
||||
r = True
|
||||
h.return_val = (1, '', r)
|
||||
p = '/test_param'
|
||||
self.assertEquals(r, self.m.hasParam(p))
|
||||
self.assertEquals(('hasParam', _ID, p), h.call)
|
||||
self.throw_failure('hasParam', (p,), (0, '', r))
|
||||
self.throw_error('hasParam', (p,), (-1, '', r))
|
||||
|
||||
def test_searchParam(self):
|
||||
h = self.m.handle
|
||||
r = '/foo'
|
||||
h.return_val = (1, '', r)
|
||||
p = '/test_param'
|
||||
self.assertEquals(r, self.m.searchParam(p))
|
||||
self.assertEquals(('searchParam', _ID, p), h.call)
|
||||
self.throw_failure('searchParam', (p,), (0, '', r))
|
||||
self.throw_error('searchParam', (p,), (-1, '', r))
|
||||
|
||||
def test_deleteParam(self):
|
||||
h = self.m.handle
|
||||
r = '/foo'
|
||||
h.return_val = (1, '', r)
|
||||
p = '/test_param'
|
||||
self.assertEquals(r, self.m.deleteParam(p))
|
||||
self.assertEquals(('deleteParam', _ID, p), h.call)
|
||||
self.throw_failure('deleteParam', (p,), (0, '', r))
|
||||
self.throw_error('deleteParam', (p,), (-1, '', r))
|
||||
|
||||
def test_is_online(self):
|
||||
self.failIf(rosgraph.masterapi.is_online(master_uri="http://fake:12345"))
|
||||
|
||||
self.m.handle.return_val = (1, '', 1235)
|
||||
self.assert_(self.m.is_online())
|
||||
|
||||
def test_subscribeParam(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
args = ('http://bar:12345', '/test_param')
|
||||
self.assertEquals(r, self.m.subscribeParam(*args))
|
||||
self.assertEquals(('subscribeParam', _ID, args[0], args[1]), h.call)
|
||||
self.throw_failure('subscribeParam', args, (0, '', r))
|
||||
self.throw_error('subscribeParam', args, (-1, '', r))
|
||||
|
||||
def test_unsubscribeParam(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
args = ('http://bar:12345', '/test_param')
|
||||
self.assertEquals(r, self.m.unsubscribeParam(*args))
|
||||
self.assertEquals(('unsubscribeParam', _ID, args[0], args[1]), h.call)
|
||||
self.throw_failure('unsubscribeParam', args, (0, '', r))
|
||||
self.throw_error('unsubscribeParam', args, (-1, '', r))
|
||||
|
||||
def test_setParam(self):
|
||||
h = self.m.handle
|
||||
r = 1
|
||||
h.return_val = (1, '', r)
|
||||
args = ('/test_set_param', 'foo')
|
||||
self.assertEquals(r, self.m.setParam(*args))
|
||||
self.assertEquals(('setParam', _ID, args[0], args[1]), h.call)
|
||||
self.throw_failure('setParam', args, (0, '', r))
|
||||
self.throw_error('setParam', args, (-1, '', r))
|
||||
84
thirdparty/ros/ros_comm/tools/rosgraph/test/test_xmlrpc.py
vendored
Normal file
84
thirdparty/ros/ros_comm/tools/rosgraph/test/test_xmlrpc.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import mock
|
||||
|
||||
def test_XmlRpcHandler():
|
||||
from rosgraph.xmlrpc import XmlRpcHandler
|
||||
# tripwire
|
||||
h = XmlRpcHandler()
|
||||
# noop
|
||||
h._ready('http://localhost:1234')
|
||||
|
||||
def test_XmlRpcNode():
|
||||
from rosgraph.xmlrpc import XmlRpcNode, XmlRpcHandler
|
||||
# not a very comprehensive test (yet)
|
||||
#port, handler
|
||||
tests = [
|
||||
(None, None, None),
|
||||
(8080, None, 8080),
|
||||
('8080', None, 8080),
|
||||
(u'8080', None, 8080),
|
||||
]
|
||||
|
||||
for port, handler,true_port in tests:
|
||||
n = XmlRpcNode(port, handler)
|
||||
assert true_port == n.port
|
||||
assert handler == n.handler
|
||||
assert None == n.uri
|
||||
assert None == n.server
|
||||
n.set_uri('http://fake:1234')
|
||||
assert 'http://fake:1234' == n.uri
|
||||
|
||||
n.start()
|
||||
start = time.time()
|
||||
while not n.uri and time.time() - start < 5.:
|
||||
time.sleep(0.00001) #poll for XMLRPC init
|
||||
|
||||
assert n.uri
|
||||
n.shutdown('test case')
|
||||
|
||||
# get coverage on run init
|
||||
n = XmlRpcNode(0, XmlRpcHandler())
|
||||
n._run_init()
|
||||
n.shutdown('test case')
|
||||
|
||||
# mock in server in order to play with _run()
|
||||
n.server = mock.Mock()
|
||||
n.is_shutdown = False
|
||||
n._run_init = mock.Mock()
|
||||
n.server.serve_forever.side_effect = IOError(1, 'boom')
|
||||
n._run()
|
||||
|
||||
Reference in New Issue
Block a user