Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a builtin nodelet manager to the server #47

Merged
merged 1 commit into from
Mar 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveralls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
service_name: travis-ci
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<build_depend>std_srvs</build_depend>

<run_depend>message_runtime</run_depend>
<run_depend>nodelet</run_depend>
<run_depend>python-yaml</run_depend>
<run_depend>roslaunch</run_depend>
<run_depend>rospy</run_depend>
Expand Down
6 changes: 6 additions & 0 deletions src/capabilities/capability_server_nodelet_manager.launch
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<launch>
<arg name="capability_server_nodelet_manager_name" default="capability_server_nodelet_manager" />
<node pkg="nodelet" type="nodelet"
name="$(arg capability_server_nodelet_manager_name)"
output="screen" args="manager" />
</launch>
26 changes: 22 additions & 4 deletions src/capabilities/launch_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,18 @@ 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):
"""Manages multiple launch files which implement capabilities"""
__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 = {}
Expand All @@ -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"""
Expand All @@ -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):
Expand Down Expand Up @@ -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."
Expand All @@ -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'
Expand All @@ -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,))

Expand Down
9 changes: 9 additions & 0 deletions src/capabilities/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
11 changes: 7 additions & 4 deletions test/rostest/test_launch_manager/test_launch_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions test/rostest/test_server/test_invalid_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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)
Expand Down