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

Source Code for Module roshlaunch.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: parent.py 9286 2010-04-21 02:50:07Z kwc $ 
 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  import sys 
 50   
 51  import roslaunch.config 
 52  from roslaunch.core import printlog_bold, printerrlog, RLException 
 53  import roslaunch.runner 
 54  import roslaunch.pmon 
 55  import roslaunch.server 
 56  import roslaunch.xmlloader  
 57   
 58  #TODO: probably move process listener infrastructure into here 
 59   
 60  # TODO: remove after wg_hardware_roslaunch has been updated 
 61  # qualification accesses this API, which has been relocated 
 62  load_config_default = roslaunch.config.load_config_default 
 63   
64 -class ROSLaunchParent(object):
65 """ 66 ROSLaunchParent represents the main 'parent' roslaunch process. It 67 is responsible for loading the launch files, assigning machines, 68 and then starting up any remote processes. The __main__ method 69 delegates most of runtime to ROSLaunchParent. 70 71 This must be called from the Python Main thread due to signal registration. 72 """ 73
74 - def __init__(self, run_id, roslaunch_files, is_core=False, port=None, 75 local_only=False, process_listeners=None, verbose=False, 76 force_screen=False, pm=None):
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 pm: Specify process monitor for parent to use 97 @type pm: roslaunch.pmon.ProcessMonitor 98 @throws RLException 99 """ 100 101 self.config = self.runner = self.server = self.remote_runner = None 102 103 self.pm = pm 104 self.logger = logging.getLogger('roslaunch.parent') 105 self.run_id = run_id 106 self.process_listeners = process_listeners 107 108 self.roslaunch_files = roslaunch_files 109 self.is_core = is_core 110 self.port = port 111 self.local_only = local_only 112 self.verbose = verbose 113 114 # I don't think we should have to pass in so many options from 115 # the outside into the roslaunch parent. One possibility is to 116 # allow alternate config loaders to be passed in. 117 self.force_screen = force_screen 118 119 # flag to prevent multiple shutdown attempts 120 self._shutting_down = False
121 122
123 - def _load_config(self):
124 self.config = roslaunch.config.load_config_default(self.roslaunch_files, self.port, verbose=self.verbose) 125 126 # #2370 (I really want to move this logic outside of parent) 127 if self.force_screen: 128 for n in self.config.nodes: 129 n.output = 'screen'
130
131 - def _start_pm(self):
132 """ 133 Start the process monitor 134 """ 135 if self.pm is None: 136 self.pm = roslaunch.pmon.start_process_monitor()
137
138 - def _init_runner(self):
139 """ 140 Initialize the roslaunch runner 141 """ 142 if self.config is None: 143 raise RLException("config is not initialized") 144 if self.pm is None: 145 raise RLException("pm is not initialized") 146 if self.server is None: 147 raise RLException("server is not initialized") 148 self.runner = roslaunch.runner.ROSLaunchRunner(self.run_id, server_uri=self.server.uri, pmon=self.pm, is_core=self.is_core, remote_runner=self.remote_runner) 149 150 # print runner info to user, put errors last to make the more visible 151 print self.config.summary(local=self.remote_runner is None) 152 if self.config: 153 for err in self.config.config_errors: 154 printerrlog("WARNING: %s"%err)
155
156 - def _start_server(self):
157 """ 158 Initialize the roslaunch parent XML-RPC server 159 """ 160 if self.config is None: 161 raise RLException("config is not initialized") 162 if self.pm is None: 163 raise RLException("pm is not initialized") 164 165 self.logger.info("starting parent XML-RPC server") 166 self.server = roslaunch.server.ROSLaunchParentNode(self.config, self.pm) 167 self.server.start() 168 if not self.server.uri: 169 raise RLException("server URI did not initialize") 170 self.logger.info("... parent XML-RPC server started")
171
172 - def _init_remote(self):
173 """ 174 Initialize the remote process runner, if required. Subroutine 175 of _start_remote, separated out for easier testing 176 """ 177 if self.config is None: 178 raise RLException("config is not initialized") 179 if self.pm is None: 180 raise RLException("pm is not initialized") 181 if self.server is None: 182 raise RLException("server is not initialized") 183 184 if not self.local_only and self.config.has_remote_nodes(): 185 # keep the remote package lazy-imported 186 import roslaunch.remote 187 self.remote_runner = roslaunch.remote.ROSRemoteRunner(self.run_id, self.config, self.pm, self.server) 188 elif self.local_only: 189 printlog_bold("LOCAL\nlocal only launch specified, will not launch remote nodes\nLOCAL\n")
190
191 - def _start_remote(self):
192 """ 193 Initializes and runs the remote process runner, if required 194 """ 195 if self.remote_runner is None: 196 self._init_remote() 197 198 if self.remote_runner is not None: 199 # start_servers() runs the roslaunch children 200 self.remote_runner.start_children()
201
202 - def _start_infrastructure(self):
203 """ 204 load config, start XMLRPC servers and process monitor 205 """ 206 if self.config is None: 207 self._load_config() 208 209 # Start the process monitor 210 if self.pm is None: 211 self._start_pm() 212 213 # Startup the roslaunch runner and XMLRPC server. 214 # Requires pm 215 if self.server is None: 216 self._start_server() 217 218 # Startup the remote infrastructure. 219 # Requires config, pm, and server 220 self._start_remote()
221
222 - def _stop_infrastructure(self):
223 """ 224 Tear down server and process monitor. Not multithread safe. 225 """ 226 #TODO more explicit teardown of remote infrastructure 227 228 # test and set flag so we don't shutdown multiple times 229 if self._shutting_down: 230 return 231 self._shutting_down = True 232 233 if self.server: 234 try: 235 self.server.shutdown("roslaunch parent complete") 236 except: 237 # don't let exceptions halt the rest of the shutdown 238 pass 239 if self.pm: 240 self.pm.shutdown() 241 self.pm.join()
242
243 - def start(self, auto_terminate=True):
244 """ 245 Run the parent roslaunch. 246 247 @param auto_terminate: stop process monitor once there are no 248 more processes to monitor (default True). This defaults to 249 True, which is the command-line behavior of roslaunch. Scripts 250 may wish to set this to False if they wish to keep the 251 roslauch infrastructure up regardless of processes being 252 monitored. 253 """ 254 self.logger.info("starting roslaunch parent run") 255 256 # load config, start XMLRPC servers and process monitor 257 try: 258 self._start_infrastructure() 259 except: 260 # infrastructure did not initialize, do teardown on whatever did come up 261 self._stop_infrastructure() 262 raise 263 264 # Initialize the actual runner. 265 # Requires config, pm, server and remote_runner 266 self._init_runner() 267 268 # Start the launch 269 self.runner.launch(self.config) 270 271 # inform process monitor that we are done with process registration 272 if auto_terminate: 273 self.pm.registrations_complete() 274 275 self.logger.info("... roslaunch parent running, waiting for process exit") 276 if self.process_listeners: 277 for l in self.process_listeners: 278 self.runner.pm.add_process_listener(l)
279
280 - def spin_once(self):
281 """ 282 Run the parent roslaunch event loop once 283 """ 284 if self.runner: 285 self.runner.spin_once()
286
287 - def spin(self):
288 """ 289 Run the parent roslaunch until exit 290 """ 291 if not self.runner: 292 raise RLException("parent not started yet") 293 try: 294 # Blocks until all processes dead/shutdown 295 self.runner.spin() 296 finally: 297 self._stop_infrastructure()
298
299 - def shutdown(self):
300 """ 301 Stop the parent roslaunch. 302 """ 303 self._stop_infrastructure()
304