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):
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 """ 105 106 self.logger = logging.getLogger('roslaunch.parent') 107 self.run_id = run_id 108 self.process_listeners = process_listeners 109 110 self.roslaunch_files = roslaunch_files 111 self.roslaunch_strs = roslaunch_strs 112 self.is_core = is_core 113 self.is_rostest = is_rostest 114 self.port = port 115 self.local_only = local_only 116 self.verbose = verbose 117 self.num_workers = num_workers 118 self.timeout = timeout 119 120 # I don't think we should have to pass in so many options from 121 # the outside into the roslaunch parent. One possibility is to 122 # allow alternate config loaders to be passed in. 123 self.force_screen = force_screen 124 125 # flag to prevent multiple shutdown attempts 126 self._shutting_down = False 127 128 self.config = self.runner = self.server = self.pm = self.remote_runner = None
129
130 - def _load_config(self):
131 self.config = roslaunch.config.load_config_default(self.roslaunch_files, self.port, 132 roslaunch_strs=self.roslaunch_strs, verbose=self.verbose) 133 134 # #2370 (I really want to move this logic outside of parent) 135 if self.force_screen: 136 for n in self.config.nodes: 137 n.output = 'screen'
138
139 - def _start_pm(self):
140 """ 141 Start the process monitor 142 """ 143 self.pm = roslaunch.pmon.start_process_monitor()
144
145 - def _init_runner(self):
146 """ 147 Initialize the roslaunch runner 148 """ 149 if self.config is None: 150 raise RLException("config is not initialized") 151 if self.pm is None: 152 raise RLException("pm is not initialized") 153 if self.server is None: 154 raise RLException("server is not initialized") 155 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) 156 157 # print runner info to user, put errors last to make the more visible 158 if self.is_core: 159 print("ros_comm version %s" % (self.config.params['/rosversion'].value)) 160 161 print(self.config.summary(local=self.remote_runner is None)) 162 if self.config: 163 for err in self.config.config_errors: 164 printerrlog("WARNING: %s"%err)
165
166 - def _start_server(self):
167 """ 168 Initialize the roslaunch parent XML-RPC server 169 """ 170 if self.config is None: 171 raise RLException("config is not initialized") 172 if self.pm is None: 173 raise RLException("pm is not initialized") 174 175 self.logger.info("starting parent XML-RPC server") 176 self.server = roslaunch.server.ROSLaunchParentNode(self.config, self.pm) 177 self.server.start() 178 if not self.server.uri: 179 raise RLException("server URI did not initialize") 180 self.logger.info("... parent XML-RPC server started")
181
182 - def _init_remote(self):
183 """ 184 Initialize the remote process runner, if required. Subroutine 185 of _start_remote, separated out for easier testing 186 """ 187 if self.config is None: 188 raise RLException("config is not initialized") 189 if self.pm is None: 190 raise RLException("pm is not initialized") 191 if self.server is None: 192 raise RLException("server is not initialized") 193 194 if not self.local_only and self.config.has_remote_nodes(): 195 # keep the remote package lazy-imported 196 import roslaunch.remote 197 self.remote_runner = roslaunch.remote.ROSRemoteRunner(self.run_id, self.config, self.pm, self.server) 198 elif self.local_only: 199 printlog_bold("LOCAL\nlocal only launch specified, will not launch remote nodes\nLOCAL\n")
200
201 - def _start_remote(self):
202 """ 203 Initializes and runs the remote process runner, if required 204 """ 205 if self.remote_runner is None: 206 self._init_remote() 207 208 if self.remote_runner is not None: 209 # start_servers() runs the roslaunch children 210 self.remote_runner.start_children()
211
212 - def _start_infrastructure(self):
213 """ 214 load config, start XMLRPC servers and process monitor 215 """ 216 if self.config is None: 217 self._load_config() 218 219 # Start the process monitor 220 if self.pm is None: 221 self._start_pm() 222 223 # Startup the roslaunch runner and XMLRPC server. 224 # Requires pm 225 if self.server is None: 226 self._start_server() 227 228 # Startup the remote infrastructure. 229 # Requires config, pm, and server 230 self._start_remote()
231
232 - def _stop_infrastructure(self):
233 """ 234 Tear down server and process monitor. Not multithread safe. 235 """ 236 #TODO more explicit teardown of remote infrastructure 237 238 # test and set flag so we don't shutdown multiple times 239 if self._shutting_down: 240 return 241 self._shutting_down = True 242 243 if self.server: 244 try: 245 self.server.shutdown("roslaunch parent complete") 246 except: 247 # don't let exceptions halt the rest of the shutdown 248 pass 249 if self.pm: 250 self.pm.shutdown() 251 self.pm.join()
252
253 - def start(self, auto_terminate=True):
254 """ 255 Run the parent roslaunch. 256 257 @param auto_terminate: stop process monitor once there are no 258 more processes to monitor (default True). This defaults to 259 True, which is the command-line behavior of roslaunch. Scripts 260 may wish to set this to False if they wish to keep the 261 roslauch infrastructure up regardless of processes being 262 monitored. 263 """ 264 self.logger.info("starting roslaunch parent run") 265 266 # load config, start XMLRPC servers and process monitor 267 try: 268 self._start_infrastructure() 269 except: 270 # infrastructure did not initialize, do teardown on whatever did come up 271 self._stop_infrastructure() 272 raise 273 274 # Initialize the actual runner. 275 # Requires config, pm, server and remote_runner 276 self._init_runner() 277 278 # Start the launch 279 self.runner.launch() 280 281 # inform process monitor that we are done with process registration 282 if auto_terminate: 283 self.pm.registrations_complete() 284 285 self.logger.info("... roslaunch parent running, waiting for process exit") 286 if self.process_listeners: 287 for l in self.process_listeners: 288 self.runner.pm.add_process_listener(l) 289 # Add listeners to server as well, otherwise they won't be 290 # called when a node on a remote machine dies. 291 self.server.add_process_listener(l)
292
293 - def spin_once(self):
294 """ 295 Run the parent roslaunch event loop once 296 """ 297 if self.runner: 298 self.runner.spin_once()
299
300 - def spin(self):
301 """ 302 Run the parent roslaunch until exit 303 """ 304 if not self.runner: 305 raise RLException("parent not started yet") 306 try: 307 # Blocks until all processes dead/shutdown 308 self.runner.spin() 309 finally: 310 self._stop_infrastructure()
311
312 - def shutdown(self):
313 """ 314 Stop the parent roslaunch. 315 """ 316 self._stop_infrastructure()
317