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