diff --git a/.coveralls.yml b/.coveralls.yml
new file mode 100644
index 0000000..9160059
--- /dev/null
+++ b/.coveralls.yml
@@ -0,0 +1 @@
+service_name: travis-ci
diff --git a/.travis.yml b/.travis.yml
index afefaf8..c5361ce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,7 +7,9 @@ install:
- sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu precise main" > /etc/apt/sources.list.d/ros-latest.list'
- wget http://packages.ros.org/ros.key -O - | sudo apt-key add -
- sudo apt-get update
- - sudo apt-get install ros-hydro-roslaunch ros-hydro-rospy ros-hydro-std-msgs ros-hydro-std-srvs ros-hydro-geometry-msgs ros-hydro-message-generation ros-hydro-message-runtime ros-hydro-catkin ros-hydro-rostest ros-hydro-rosservice
+ - sudo apt-get install ros-hydro-nodelet ros-hydro-roslaunch ros-hydro-rospy ros-hydro-std-msgs ros-hydro-std-srvs ros-hydro-geometry-msgs ros-hydro-message-generation ros-hydro-message-runtime ros-hydro-catkin ros-hydro-rostest ros-hydro-rosservice
+# Install image_proc to get a nodelet instaleld as a workaround to https://github.com/ros/pluginlib/pull/22
+ - sudo apt-get install ros-hydro-image-proc
# command to run tests
script:
- source /opt/ros/hydro/setup.bash
diff --git a/Makefile b/Makefile
index 0ef19fa..81749b8 100644
--- a/Makefile
+++ b/Makefile
@@ -17,8 +17,7 @@ coverage:
-rm ~/.ros/.coverage
-rm ${BUILD_DIR}/.coverage
-rm ./.coverage
- cd ${BUILD_DIR} && mkdir src
- ln -s ${SRC_DIR} ${BUILD_DIR}/src
+ -ln -s ${SRC_DIR} ${BUILD_DIR}/src
cd ${BUILD_DIR} && catkin_make
cd ${BUILD_DIR} && catkin_make tests
cd ${BUILD_DIR} && catkin_make -j1 run_tests
diff --git a/package.xml b/package.xml
index 518b58b..7868ebb 100644
--- a/package.xml
+++ b/package.xml
@@ -25,6 +25,7 @@
std_srvs
message_runtime
+ nodelet
python-yaml
roslaunch
rospy
diff --git a/src/capabilities/capability_server_nodelet_manager.launch b/src/capabilities/capability_server_nodelet_manager.launch
new file mode 100644
index 0000000..7d2aa69
--- /dev/null
+++ b/src/capabilities/capability_server_nodelet_manager.launch
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/src/capabilities/launch_manager.py b/src/capabilities/launch_manager.py
index d69ab2f..04388b5 100644
--- a/src/capabilities/launch_manager.py
+++ b/src/capabilities/launch_manager.py
@@ -75,7 +75,10 @@ def is_exe(fpath):
return exe_file
return None
-_placeholder_script = os.path.join(os.path.dirname(__file__), 'placeholder_script')
+_this_dir = os.path.dirname(__file__)
+_placeholder_script = os.path.join(_this_dir, 'placeholder_script')
+_nodelet_manager_launch_file = os.path.join(_this_dir, 'capability_server_nodelet_manager.launch')
+_special_nodelet_manager_capability = '!!nodelet_manager'
class LaunchManager(object):
@@ -83,7 +86,7 @@ class LaunchManager(object):
__roslaunch_exec = which('roslaunch')
__python_exec = which('python')
- def __init__(self, quiet=False, screen=False):
+ def __init__(self, quiet=False, screen=False, nodelet_manager_name=None):
self.__running_launch_files_lock = threading.Lock()
with self.__running_launch_files_lock:
self.__running_launch_files = {}
@@ -94,6 +97,8 @@ def __init__(self, quiet=False, screen=False):
self.stopping = False
self.__quiet = quiet
self.__screen = screen
+ self.__nodelet_manager_name = nodelet_manager_name or (rospy.get_name().lstrip('/') + '_nodelet_manager')
+ self.__start_nodelet_manager()
def stop(self):
"""Stops the launch manager, also stopping any running launch files"""
@@ -112,8 +117,9 @@ def __stop_by_pid(self, pid):
if pid not in self.__running_launch_files:
raise RuntimeError("No running launch file with PID of '{0}'".format(pid))
proc, thread, _, _ = self.__running_launch_files[pid]
- proc.terminate()
- proc.wait()
+ if proc.poll() is None:
+ proc.terminate()
+ proc.wait()
thread.join()
def stop_capability_provider(self, pid):
@@ -157,6 +163,9 @@ def run_capability_provider(self, provider, provider_path):
.format(provider.name))
else:
launch_file = os.path.join(provider_path, provider.launch_file)
+ self.run_launch_file(launch_file, provider)
+
+ def run_launch_file(self, launch_file, provider):
with self.__running_launch_files_lock:
if launch_file is not None and launch_file in [x[3] for x in self.__running_launch_files.values()]:
raise RuntimeError("Launch file at '{0}' is already running."
@@ -168,6 +177,7 @@ def run_capability_provider(self, provider, provider_path):
cmd = [self.__roslaunch_exec, '--screen', launch_file]
else:
cmd = [self.__roslaunch_exec, launch_file]
+ cmd.append("capability_server_nodelet_manager_name:=" + self.__nodelet_manager_name)
if self.__quiet:
env = copy.deepcopy(os.environ)
env['PYTHONUNBUFFERED'] = 'x'
@@ -187,6 +197,14 @@ def run_capability_provider(self, provider, provider_path):
self.__event_publisher.publish(msg)
thread.start()
+ def __start_nodelet_manager(self):
+ class MockProvider:
+ implements = _special_nodelet_manager_capability
+ name = rospy.get_name().lstrip('/')
+ provider = MockProvider()
+ launch_file = _nodelet_manager_launch_file
+ self.run_launch_file(launch_file, provider)
+
def __start_communication_thread(self, proc):
return threading.Thread(target=self.__monitor_process, args=(proc,))
diff --git a/src/capabilities/server.py b/src/capabilities/server.py
index f0514c8..39ce0be 100644
--- a/src/capabilities/server.py
+++ b/src/capabilities/server.py
@@ -76,6 +76,7 @@
from capabilities.discovery import spec_file_index_from_package_index
from capabilities.discovery import spec_index_from_spec_file_index
+from capabilities.launch_manager import _special_nodelet_manager_capability
from capabilities.launch_manager import LaunchManager
from capabilities.msg import Capability
@@ -473,6 +474,14 @@ def _handle_capability_events(self, event):
# Ignore the `server_ready` event
if event.type == event.SERVER_READY:
return
+ # Specially handle the nodelet manager
+ if event.capability == _special_nodelet_manager_capability:
+ if event.type == event.LAUNCHED:
+ return
+ elif event.type == event.TERMINATED:
+ if not rospy.is_shutdown():
+ rospy.logerr("Capability server's nodelet manager terminated unexpectedly.")
+ self.shutdown()
# Update the capability
capability = event.capability
with self.__graph_lock:
diff --git a/test/rostest/test_launch_manager/test_launch_manager.py b/test/rostest/test_launch_manager/test_launch_manager.py
index dcbeb69..658ee1d 100755
--- a/test/rostest/test_launch_manager/test_launch_manager.py
+++ b/test/rostest/test_launch_manager/test_launch_manager.py
@@ -77,10 +77,13 @@ def test_launch_manager_no_launch_file_provider(self):
def test_process_monitoring(self):
lm = launch_manager.LaunchManager()
- with assert_raises_regex(RuntimeError, 'Unknown process id'):
- proc = Mock()
- proc.pid = -1
- lm._LaunchManager__monitor_process(proc)
+ try:
+ with assert_raises_regex(RuntimeError, 'Unknown process id'):
+ proc = Mock()
+ proc.pid = -1
+ lm._LaunchManager__monitor_process(proc)
+ finally:
+ lm.stop()
if __name__ == '__main__':
import rospy
diff --git a/test/rostest/test_server/test_invalid_specs.py b/test/rostest/test_server/test_invalid_specs.py
index 69a09d4..82bde51 100755
--- a/test/rostest/test_server/test_invalid_specs.py
+++ b/test/rostest/test_server/test_invalid_specs.py
@@ -28,6 +28,7 @@ def test_invalid_specs(self):
capability_server._CapabilityServer__load_capabilities()
capability_server._CapabilityServer__populate_default_providers()
capability_server._CapabilityServer__stop_capability('not_a_running_capability')
+ capability_server.shutdown()
def test_no_default_provider_pedantic(self):
no_default_provider = os.path.join(TEST_DIR, 'rostest', 'test_server', 'no_default_provider')
@@ -37,6 +38,7 @@ def test_no_default_provider_pedantic(self):
capability_server._CapabilityServer__load_capabilities()
with assert_raises(SystemExit):
capability_server._CapabilityServer__populate_default_providers()
+ capability_server.shutdown()
def test_no_default_provider(self):
no_default_provider = os.path.join(TEST_DIR, 'rostest', 'test_server', 'no_default_provider')
@@ -45,6 +47,7 @@ def test_no_default_provider(self):
capability_server = server.CapabilityServer(ros_package_path)
capability_server._CapabilityServer__load_capabilities()
capability_server._CapabilityServer__populate_default_providers()
+ capability_server.shutdown()
def test_invalid_default_provider(self):
minimal_dir = os.path.join(TEST_DIR, 'unit', 'discovery_workspaces', 'minimal')
@@ -54,6 +57,7 @@ def test_invalid_default_provider(self):
capability_server._CapabilityServer__load_capabilities()
with assert_raises(SystemExit):
capability_server._CapabilityServer__populate_default_providers()
+ capability_server.shutdown()
def test_wrong_default_provider(self):
dc_dir = os.path.join(TEST_DIR, 'unit', 'discovery_workspaces', 'dependent_capabilities')
@@ -64,6 +68,7 @@ def test_wrong_default_provider(self):
capability_server._CapabilityServer__load_capabilities()
with assert_raises(SystemExit):
capability_server._CapabilityServer__populate_default_providers()
+ capability_server.shutdown()
def test_event_handler(self):
invalid_specs_dir = os.path.join(TEST_DIR, 'unit', 'discovery_workspaces', 'invalid_specs')
@@ -80,6 +85,7 @@ def test_event_handler(self):
msg.type = 'doesnt matter'
pub.publish(msg)
rospy.sleep(1) # Allow time for the publish to happen
+ capability_server.shutdown()
if __name__ == '__main__':
rospy.init_node(TEST_NAME, anonymous=True)