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