Package roslaunch :: Module parent
[frames] | no frames]

Source Code for Module roslaunch.parent

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id$ 
 34   
 35  """ 
 36  roslaunch.parent providees the L{ROSLaunchParent} implementation, 
 37  which represents the main 'parent' roslaunch process.  
 38   
 39  ROSLaunch has a client/server architecture for running remote 
 40  processes. When a user runs roslaunch, this creates a "parent" 
 41  roslaunch process, which is responsible for managing local 
 42  processes. This parent process will also start "child" processes on 
 43  remote machines. The parent can then invoke methods on this child 
 44  process to launch remote processes, and the child can invoke methods 
 45  on the parent to provide feedback. 
 46  """ 
 47   
 48  import logging 
 49   
 50  import roslaunch.config 
 51  from roslaunch.core import printlog_bold, printerrlog, RLException 
 52  import roslaunch.launch 
 53  import roslaunch.pmon 
 54  import roslaunch.server 
 55  import roslaunch.xmlloader  
 56   
 57  from rosmaster.master_api import NUM_WORKERS 
 58   
 59  #TODO: probably move process listener infrastructure into here 
 60   
 61  # TODO: remove after wg_hardware_roslaunch has been updated 
 62  # qualification accesses this API, which has been relocated 
 63  load_config_default = roslaunch.config.load_config_default 
 64   
65 -class ROSLaunchParent(object):
66 """ 67 ROSLaunchParent represents the main 'parent' roslaunch process. It 68 is responsible for loading the launch files, assigning machines, 69 and then starting up any remote processes. The __main__ method 70 delegates most of runtime to ROSLaunchParent. 71 72 This must be called from the Python Main thread due to signal registration. 73 """ 74
75 - def __init__(self, run_id, roslaunch_files, is_core=False, port=None, local_only=False, process_listeners=None, 76 verbose=False, force_screen=False, is_rostest=False, roslaunch_strs=None, num_workers=NUM_WORKERS, timeout=None, master_logger_level=False):
77 """ 78 @param run_id: UUID of roslaunch session 79 @type run_id: str 80 @param roslaunch_files: list of launch configuration 81 files to load 82 @type roslaunch_files: [str] 83 @param is_core bool: if True, this launch is a roscore 84 instance. This affects the error behavior if a master is 85 already running (i.e. it fails). 86 @type is_core: bool 87 @param process_listeners: (optional) list of process listeners 88 to register with process monitor once launch is running 89 @type process_listeners: [L{roslaunch.pmon.ProcessListener}] 90 @param port: (optional) override master port number from what is specified in the master URI. 91 @type port: int 92 @param verbose: (optional) print verbose output 93 @type verbose: boolean 94 @param force_screen: (optional) force output of all nodes to screen 95 @type force_screen: boolean 96 @param is_rostest bool: if True, this launch is a rostest 97 instance. This affects validation checks. 98 @type is_rostest: bool 99 @param num_workers: If this is the core, the number of worker-threads to use. 100 @type num_workers: int 101 @param timeout: If this is the core, the socket-timeout to use. 102 @type timeout: Float or None 103 @throws RLException 104 @param master_logger_level: Specify roscore's rosmaster.master logger level, use default if it is False. 105 @type master_logger_level: str or False 106 """ 107 108 self.logger = logging.getLogger('roslaunch.parent') 109 self.run_id = run_id 110 self.process_listeners = process_listeners 111 112 self.roslaunch_files = roslaunch_files 113 self.roslaunch_strs = roslaunch_strs 114 self.is_core = is_core 115 self.is_rostest = is_rostest 116 self.port = port 117 self.local_only = local_only 118 self.verbose = verbose 119 self.num_workers = num_workers 120 self.timeout = timeout 121 self.master_logger_level = master_logger_level 122 123 # I don't think we should have to pass in so many options from 124 # the outside into the roslaunch parent. One possibility is to 125 # allow alternate config loaders to be passed in. 126 self.force_screen = force_screen 127 128 # flag to prevent multiple shutdown attempts 129 self._shutting_down = False 130 131 self.config = self.runner = self.server = self.pm = self.remote_runner = None
132
133 - def _load_config(self):
134 self.config = roslaunch.config.load_config_default(self.roslaunch_files, self.port, 135 roslaunch_strs=self.roslaunch_strs, verbose=self.verbose) 136 137 # #2370 (I really want to move this logic outside of parent) 138 if self.force_screen: 139 for n in self.config.nodes: 140 n.output = 'screen'
141
142 - def _start_pm(self):
143 """ 144 Start the process monitor 145 """ 146 self.pm = roslaunch.pmon.start_process_monitor()
147
148 - def _init_runner(self):
149 """ 150 Initialize the roslaunch runner 151 """ 152 if self.config is None: 153 raise RLException("config is not initialized") 154 if self.pm is None: 155 raise RLException("pm is not initialized") 156 if self.server is None: 157 raise RLException("server is not initialized") 158 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, master_logger_level=self.master_logger_level) 159 160 # print runner info to user, put errors last to make the more visible 161 if self.is_core: 162 print("ros_comm version %s" % (self.config.params['/rosversion'].value)) 163 164 print(self.config.summary(local=self.remote_runner is None)) 165 if self.config: 166 for err in self.config.config_errors: 167 printerrlog("WARNING: %s"%err)
168
169 - def _start_server(self):
170 """ 171 Initialize the roslaunch parent XML-RPC server 172 """ 173 if self.config is None: 174 raise RLException("config is not initialized") 175 if self.pm is None: 176 raise RLException("pm is not initialized") 177 178 self.logger.info("starting parent XML-RPC server") 179 self.server = roslaunch.server.ROSLaunchParentNode(self.config, self.pm) 180 self.server.start() 181 if not self.server.uri: 182 raise RLException("server URI did not initialize") 183 self.logger.info("... parent XML-RPC server started")
184
185 - def _init_remote(self):
186 """ 187 Initialize the remote process runner, if required. Subroutine 188 of _start_remote, separated out for easier testing 189 """ 190 if self.config is None: 191 raise RLException("config is not initialized") 192 if self.pm is None: 193 raise RLException("pm is not initialized") 194 if self.server is None: 195 raise RLException("server is not initialized") 196 197 if not self.local_only and self.config.has_remote_nodes(): 198 # keep the remote package lazy-imported 199 import roslaunch.remote 200 self.remote_runner = roslaunch.remote.ROSRemoteRunner(self.run_id, self.config, self.pm, self.server) 201 elif self.local_only: 202 printlog_bold("LOCAL\nlocal only launch specified, will not launch remote nodes\nLOCAL\n")
203
204 - def _start_remote(self):
205 """ 206 Initializes and runs the remote process runner, if required 207 """ 208 if self.remote_runner is None: 209 self._init_remote() 210 211 if self.remote_runner is not None: 212 # start_servers() runs the roslaunch children 213 self.remote_runner.start_children()
214
215 - def _start_infrastructure(self):
216 """ 217 load config, start XMLRPC servers and process monitor 218 """ 219 if self.config is None: 220 self._load_config() 221 222 # Start the process monitor 223 if self.pm is None: 224 self._start_pm() 225 226 # Startup the roslaunch runner and XMLRPC server. 227 # Requires pm 228 if self.server is None: 229 self._start_server() 230 231 # Startup the remote infrastructure. 232 # Requires config, pm, and server 233 self._start_remote()
234
235 - def _stop_infrastructure(self):
236 """ 237 Tear down server and process monitor. Not multithread safe. 238 """ 239 #TODO more explicit teardown of remote infrastructure 240 241 # test and set flag so we don't shutdown multiple times 242 if self._shutting_down: 243 return 244 self._shutting_down = True 245 246 if self.server: 247 try: 248 self.server.shutdown("roslaunch parent complete") 249 except: 250 # don't let exceptions halt the rest of the shutdown 251 pass 252 if self.pm: 253 self.pm.shutdown() 254 self.pm.join()
255
256 - def start(self, auto_terminate=True):
257 """ 258 Run the parent roslaunch. 259 260 @param auto_terminate: stop process monitor once there are no 261 more processes to monitor (default True). This defaults to 262 True, which is the command-line behavior of roslaunch. Scripts 263 may wish to set this to False if they wish to keep the 264 roslauch infrastructure up regardless of processes being 265 monitored. 266 """ 267 self.logger.info("starting roslaunch parent run") 268 269 # load config, start XMLRPC servers and process monitor 270 try: 271 self._start_infrastructure() 272 except: 273 # infrastructure did not initialize, do teardown on whatever did come up 274 self._stop_infrastructure() 275 raise 276 277 # Initialize the actual runner. 278 # Requires config, pm, server and remote_runner 279 self._init_runner() 280 281 # Start the launch 282 self.runner.launch() 283 284 # inform process monitor that we are done with process registration 285 if auto_terminate: 286 self.pm.registrations_complete() 287 288 self.logger.info("... roslaunch parent running, waiting for process exit") 289 if self.process_listeners: 290 for l in self.process_listeners: 291 self.runner.pm.add_process_listener(l) 292 # Add listeners to server as well, otherwise they won't be 293 # called when a node on a remote machine dies. 294 self.server.add_process_listener(l)
295
296 - def spin_once(self):
297 """ 298 Run the parent roslaunch event loop once 299 """ 300 if self.runner: 301 self.runner.spin_once()
302
303 - def spin(self):
304 """ 305 Run the parent roslaunch until exit 306 """ 307 if not self.runner: 308 raise RLException("parent not started yet") 309 try: 310 # Blocks until all processes dead/shutdown 311 self.runner.spin() 312 finally: 313 self._stop_infrastructure()
314
315 - def shutdown(self):
316 """ 317 Stop the parent roslaunch. 318 """ 319 self._stop_infrastructure()
320