/usr/lib/python2.7/dist-packages/roslaunch/parent.py is in python-roslaunch 1.11.16-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | # 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$
"""
roslaunch.parent providees the L{ROSLaunchParent} implementation,
which represents the main 'parent' roslaunch process.
ROSLaunch has a client/server architecture for running remote
processes. When a user runs roslaunch, this creates a "parent"
roslaunch process, which is responsible for managing local
processes. This parent process will also start "child" processes on
remote machines. The parent can then invoke methods on this child
process to launch remote processes, and the child can invoke methods
on the parent to provide feedback.
"""
import logging
import roslaunch.config
from roslaunch.core import printlog_bold, printerrlog, RLException
import roslaunch.launch
import roslaunch.pmon
import roslaunch.server
import roslaunch.xmlloader
from rosmaster.master_api import NUM_WORKERS
#TODO: probably move process listener infrastructure into here
# TODO: remove after wg_hardware_roslaunch has been updated
# qualification accesses this API, which has been relocated
load_config_default = roslaunch.config.load_config_default
class ROSLaunchParent(object):
"""
ROSLaunchParent represents the main 'parent' roslaunch process. It
is responsible for loading the launch files, assigning machines,
and then starting up any remote processes. The __main__ method
delegates most of runtime to ROSLaunchParent.
This must be called from the Python Main thread due to signal registration.
"""
def __init__(self, run_id, roslaunch_files, is_core=False, port=None, local_only=False, process_listeners=None,
verbose=False, force_screen=False, is_rostest=False, roslaunch_strs=None, num_workers=NUM_WORKERS, timeout=None):
"""
@param run_id: UUID of roslaunch session
@type run_id: str
@param roslaunch_files: list of launch configuration
files to load
@type roslaunch_files: [str]
@param is_core bool: if True, this launch is a roscore
instance. This affects the error behavior if a master is
already running (i.e. it fails).
@type is_core: bool
@param process_listeners: (optional) list of process listeners
to register with process monitor once launch is running
@type process_listeners: [L{roslaunch.pmon.ProcessListener}]
@param port: (optional) override master port number from what is specified in the master URI.
@type port: int
@param verbose: (optional) print verbose output
@type verbose: boolean
@param force_screen: (optional) force output of all nodes to screen
@type force_screen: boolean
@param is_rostest bool: if True, this launch is a rostest
instance. This affects validation checks.
@type is_rostest: bool
@param num_workers: If this is the core, the number of worker-threads to use.
@type num_workers: int
@param timeout: If this is the core, the socket-timeout to use.
@type timeout: Float or None
@throws RLException
"""
self.logger = logging.getLogger('roslaunch.parent')
self.run_id = run_id
self.process_listeners = process_listeners
self.roslaunch_files = roslaunch_files
self.roslaunch_strs = roslaunch_strs
self.is_core = is_core
self.is_rostest = is_rostest
self.port = port
self.local_only = local_only
self.verbose = verbose
self.num_workers = num_workers
self.timeout = timeout
# I don't think we should have to pass in so many options from
# the outside into the roslaunch parent. One possibility is to
# allow alternate config loaders to be passed in.
self.force_screen = force_screen
# flag to prevent multiple shutdown attempts
self._shutting_down = False
self.config = self.runner = self.server = self.pm = self.remote_runner = None
def _load_config(self):
self.config = roslaunch.config.load_config_default(self.roslaunch_files, self.port,
roslaunch_strs=self.roslaunch_strs, verbose=self.verbose)
# #2370 (I really want to move this logic outside of parent)
if self.force_screen:
for n in self.config.nodes:
n.output = 'screen'
def _start_pm(self):
"""
Start the process monitor
"""
self.pm = roslaunch.pmon.start_process_monitor()
def _init_runner(self):
"""
Initialize the roslaunch runner
"""
if self.config is None:
raise RLException("config is not initialized")
if self.pm is None:
raise RLException("pm is not initialized")
if self.server is None:
raise RLException("server is not initialized")
self.runner = roslaunch.launch.ROSLaunchRunner(self.run_id, self.config, server_uri=self.server.uri, pmon=self.pm, is_core=self.is_core, remote_runner=self.remote_runner, is_rostest=self.is_rostest, num_workers=self.num_workers, timeout=self.timeout)
# print runner info to user, put errors last to make the more visible
if self.is_core:
print("ros_comm version %s" % (self.config.params['/rosversion'].value))
print(self.config.summary(local=self.remote_runner is None))
if self.config:
for err in self.config.config_errors:
printerrlog("WARNING: %s"%err)
def _start_server(self):
"""
Initialize the roslaunch parent XML-RPC server
"""
if self.config is None:
raise RLException("config is not initialized")
if self.pm is None:
raise RLException("pm is not initialized")
self.logger.info("starting parent XML-RPC server")
self.server = roslaunch.server.ROSLaunchParentNode(self.config, self.pm)
self.server.start()
if not self.server.uri:
raise RLException("server URI did not initialize")
self.logger.info("... parent XML-RPC server started")
def _init_remote(self):
"""
Initialize the remote process runner, if required. Subroutine
of _start_remote, separated out for easier testing
"""
if self.config is None:
raise RLException("config is not initialized")
if self.pm is None:
raise RLException("pm is not initialized")
if self.server is None:
raise RLException("server is not initialized")
if not self.local_only and self.config.has_remote_nodes():
# keep the remote package lazy-imported
import roslaunch.remote
self.remote_runner = roslaunch.remote.ROSRemoteRunner(self.run_id, self.config, self.pm, self.server)
elif self.local_only:
printlog_bold("LOCAL\nlocal only launch specified, will not launch remote nodes\nLOCAL\n")
def _start_remote(self):
"""
Initializes and runs the remote process runner, if required
"""
if self.remote_runner is None:
self._init_remote()
if self.remote_runner is not None:
# start_servers() runs the roslaunch children
self.remote_runner.start_children()
def _start_infrastructure(self):
"""
load config, start XMLRPC servers and process monitor
"""
if self.config is None:
self._load_config()
# Start the process monitor
if self.pm is None:
self._start_pm()
# Startup the roslaunch runner and XMLRPC server.
# Requires pm
if self.server is None:
self._start_server()
# Startup the remote infrastructure.
# Requires config, pm, and server
self._start_remote()
def _stop_infrastructure(self):
"""
Tear down server and process monitor. Not multithread safe.
"""
#TODO more explicit teardown of remote infrastructure
# test and set flag so we don't shutdown multiple times
if self._shutting_down:
return
self._shutting_down = True
if self.server:
try:
self.server.shutdown("roslaunch parent complete")
except:
# don't let exceptions halt the rest of the shutdown
pass
if self.pm:
self.pm.shutdown()
self.pm.join()
def start(self, auto_terminate=True):
"""
Run the parent roslaunch.
@param auto_terminate: stop process monitor once there are no
more processes to monitor (default True). This defaults to
True, which is the command-line behavior of roslaunch. Scripts
may wish to set this to False if they wish to keep the
roslauch infrastructure up regardless of processes being
monitored.
"""
self.logger.info("starting roslaunch parent run")
# load config, start XMLRPC servers and process monitor
try:
self._start_infrastructure()
except:
# infrastructure did not initialize, do teardown on whatever did come up
self._stop_infrastructure()
raise
# Initialize the actual runner.
# Requires config, pm, server and remote_runner
self._init_runner()
# Start the launch
self.runner.launch()
# inform process monitor that we are done with process registration
if auto_terminate:
self.pm.registrations_complete()
self.logger.info("... roslaunch parent running, waiting for process exit")
if self.process_listeners:
for l in self.process_listeners:
self.runner.pm.add_process_listener(l)
def spin_once(self):
"""
Run the parent roslaunch event loop once
"""
if self.runner:
self.runner.spin_once()
def spin(self):
"""
Run the parent roslaunch until exit
"""
if not self.runner:
raise RLException("parent not started yet")
try:
# Blocks until all processes dead/shutdown
self.runner.spin()
finally:
self._stop_infrastructure()
def shutdown(self):
"""
Stop the parent roslaunch.
"""
self._stop_infrastructure()
|