Package roslaunch
[frames] | no frames]

Source Code for Package roslaunch

  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  from __future__ import print_function 
 36   
 37  import os 
 38  import logging 
 39  import rospkg 
 40  import sys 
 41  import traceback 
 42   
 43  # monkey-patch to suppress threading error message in Python 2.7.3 
 44  # see http://stackoverflow.com/questions/13193278/understand-python-threading-bug 
 45  if sys.version_info[:3] == (2, 7, 3): 
 46      import threading 
 47      threading._DummyThread._Thread__stop = lambda _dummy: None 
 48   
 49  import rospkg 
 50   
 51  from . import core as roslaunch_core 
 52  from . import param_dump as roslaunch_param_dump 
 53   
 54  # symbol exports 
 55  from .core import Node, Test, Master, RLException 
 56  from .config import ROSLaunchConfig 
 57  from .launch import ROSLaunchRunner 
 58  from .xmlloader import XmlLoader, XmlParseException 
 59   
 60   
 61  # script api 
 62  from .scriptapi import ROSLaunch 
 63  from .pmon import Process 
 64   
 65  try: 
 66      from rosmaster import DEFAULT_MASTER_PORT 
 67  except: 
 68      DEFAULT_MASTER_PORT = 11311 
 69   
 70  from rosmaster.master_api import NUM_WORKERS 
 71  from roslaunch.nodeprocess import DEFAULT_TIMEOUT_SIGINT, DEFAULT_TIMEOUT_SIGTERM 
 72   
 73  NAME = 'roslaunch' 
 74   
75 -def configure_logging(uuid):
76 """ 77 scripts using roslaunch MUST call configure_logging 78 """ 79 try: 80 import socket 81 import rosgraph.roslogging 82 logfile_basename = os.path.join(uuid, '%s-%s-%s.log'%(NAME, socket.gethostname(), os.getpid())) 83 # additional: names of python packages we depend on that may also be logging 84 logfile_name = rosgraph.roslogging.configure_logging(NAME, filename=logfile_basename) 85 if logfile_name: 86 print("... logging to %s"%logfile_name) 87 88 # add logger to internal roslaunch logging infrastructure 89 logger = logging.getLogger('roslaunch') 90 roslaunch_core.add_printlog_handler(logger.info) 91 roslaunch_core.add_printerrlog_handler(logger.error) 92 except: 93 print("WARNING: unable to configure logging. No log files will be generated", file=sys.stderr)
94
95 -def write_pid_file(options_pid_fn, options_core, port):
96 if options_pid_fn or options_core: 97 # #2987 98 ros_home = rospkg.get_ros_home() 99 if options_pid_fn: 100 pid_fn = os.path.expanduser(options_pid_fn) 101 if os.path.dirname(pid_fn) == ros_home and not os.path.exists(ros_home): 102 os.makedirs(ros_home) 103 else: 104 # NOTE: this assumption is not 100% valid until work on #3097 is complete 105 if port is None: 106 port = DEFAULT_MASTER_PORT 107 pid_fn = os.path.join(ros_home, 'roscore-%s.pid'%(port)) 108 # #3828 109 if not os.path.exists(ros_home): 110 os.makedirs(ros_home) 111 112 with open(pid_fn, "w") as f: 113 f.write(str(os.getpid()))
114
115 -def _get_optparse():
116 from optparse import OptionParser 117 118 usage = "usage: %prog [options] [package] <filename> [arg_name:=value...]\n" 119 usage += " %prog [options] <filename> [<filename>...] [arg_name:=value...]\n\n" 120 usage += "If <filename> is a single dash ('-'), launch XML is read from standard input." 121 parser = OptionParser(usage=usage, prog=NAME) 122 parser.add_option("--files", 123 dest="file_list", default=False, action="store_true", 124 help="Print list files loaded by launch file, including launch file itself") 125 parser.add_option("--args", 126 dest="node_args", default=None, 127 help="Print command-line arguments for node", metavar="NODE_NAME") 128 parser.add_option("--nodes", 129 dest="node_list", default=False, action="store_true", 130 help="Print list of node names in launch file") 131 parser.add_option("--find-node", 132 dest="find_node", default=None, 133 help="Find launch file that node is defined in", metavar="NODE_NAME") 134 parser.add_option("-c", "--child", 135 dest="child_name", default=None, 136 help="Run as child service 'NAME'. Required with -u", metavar="NAME") 137 parser.add_option("--local", 138 dest="local_only", default=False, action="store_true", 139 help="Do not launch remote nodes") 140 # #2370 141 parser.add_option("--screen", 142 dest="force_screen", default=False, action="store_true", 143 help="Force output of all local nodes to screen") 144 parser.add_option("--required", 145 dest="force_required", default=False, action="store_true", 146 help="Force all nodes to be required") 147 parser.add_option("--log", 148 dest="force_log", default=False, action="store_true", 149 help="Force output of all local nodes to log") 150 parser.add_option("-u", "--server_uri", 151 dest="server_uri", default=None, 152 help="URI of server. Required with -c", metavar="URI") 153 parser.add_option("--run_id", 154 dest="run_id", default=None, 155 help="run_id of session. Required with -c", metavar="RUN_ID") 156 # #1254: wait until master comes online before starting 157 parser.add_option("--wait", action="store_true", 158 dest="wait_for_master", default=False, 159 help="wait for master to start before launching") 160 parser.add_option("-p", "--port", 161 dest="port", default=None, 162 help="master port. Only valid if master is launched", metavar="PORT") 163 parser.add_option("--core", action="store_true", 164 dest="core", default=False, 165 help="Launch core services only") 166 parser.add_option("--pid", 167 dest="pid_fn", default="", 168 help="write the roslaunch pid to filename") 169 parser.add_option("-v", action="store_true", 170 dest="verbose", default=False, 171 help="verbose printing") 172 parser.add_option("--no-summary", action="store_true", 173 dest="no_summary", default=False, 174 help="hide summary printing") 175 # 2685 - Dump parameters of launch files 176 parser.add_option("--dump-params", default=False, action="store_true", 177 dest="dump_params", 178 help="Dump parameters of all roslaunch files to stdout") 179 parser.add_option("--skip-log-check", default=False, action="store_true", 180 dest="skip_log_check", 181 help="skip check size of log folder") 182 parser.add_option("--ros-args", default=False, action="store_true", 183 dest="ros_args", 184 help="Display command-line arguments for this launch file") 185 parser.add_option("--disable-title", default=False, action="store_true", 186 dest="disable_title", 187 help="Disable setting of terminal title") 188 parser.add_option("-w", "--numworkers", 189 dest="num_workers", default=NUM_WORKERS, type=int, 190 help="override number of worker threads. Only valid for core services.", metavar="NUM_WORKERS") 191 parser.add_option("-t", "--timeout", 192 dest="timeout", 193 help="override the socket connection timeout (in seconds). Only valid for core services.", metavar="TIMEOUT") 194 parser.add_option("--master-logger-level", 195 dest="master_logger_level", default=False, type=str, 196 help="set rosmaster.master logger level ('debug', 'info', 'warn', 'error', 'fatal')") 197 parser.add_option("--sigint-timeout", 198 dest="sigint_timeout", 199 default=DEFAULT_TIMEOUT_SIGINT, type=float, 200 help="the SIGINT timeout used when killing nodes (in seconds).", 201 metavar="SIGINT_TIMEOUT") 202 parser.add_option("--sigterm-timeout", 203 dest="sigterm_timeout", 204 default=DEFAULT_TIMEOUT_SIGTERM, type=float, 205 help="the SIGTERM timeout used when killing nodes if SIGINT does not stop the node (in seconds).", 206 metavar="SIGTERM_TIMEOUT") 207 208 return parser
209
210 -def _validate_args(parser, options, args):
211 # validate args first so we don't spin up any resources 212 if options.child_name: 213 if not options.server_uri: 214 parser.error("--child option requires --server_uri to be set as well") 215 if not options.run_id: 216 parser.error("--child option requires --run_id to be set as well") 217 if options.port: 218 parser.error("port option cannot be used with roslaunch child mode") 219 if args: 220 parser.error("Input files are not allowed when run in child mode") 221 elif options.core: 222 if args: 223 parser.error("Input files are not allowed when launching core") 224 if options.run_id: 225 parser.error("--run_id should only be set for child roslaunches (-c)") 226 227 # we don't actually do anything special for core as the roscore.xml file 228 # is an implicit include for any roslaunch 229 230 elif len(args) == 0: 231 parser.error("you must specify at least one input file") 232 else: 233 missing_files = [f for f in args if not (f == '-' or os.path.exists(f))] 234 if missing_files: 235 parser.error("The following input files do not exist: %s"%', '.join(missing_files)) 236 237 if args.count('-') > 1: 238 parser.error("Only a single instance of the dash ('-') may be specified.") 239 240 if len([x for x in [options.node_list, options.find_node, options.node_args, options.ros_args] if x]) > 1: 241 parser.error("only one of [--nodes, --find-node, --args --ros-args] may be specified")
242
243 -def handle_exception(roslaunch_core, logger, msg, e):
244 roslaunch_core.printerrlog(msg + str(e)) 245 roslaunch_core.printerrlog('The traceback for the exception was written to the log file') 246 if logger: 247 logger.error(traceback.format_exc()) 248 sys.exit(1)
249
250 -def main(argv=sys.argv):
251 options = None 252 logger = None 253 try: 254 from . import rlutil 255 parser = _get_optparse() 256 257 (options, args) = parser.parse_args(argv[1:]) 258 args = rlutil.resolve_launch_arguments(args) 259 _validate_args(parser, options, args) 260 261 # node args doesn't require any roslaunch infrastructure, so process it first 262 if any([options.node_args, options.node_list, options.find_node, options.dump_params, options.file_list, options.ros_args]): 263 if options.node_args and not args: 264 parser.error("please specify a launch file") 265 266 from . import node_args 267 if options.node_args: 268 node_args.print_node_args(options.node_args, args) 269 elif options.find_node: 270 node_args.print_node_filename(options.find_node, args) 271 # Dump parameters, #2685 272 elif options.dump_params: 273 roslaunch_param_dump.dump_params(args) 274 elif options.file_list: 275 rlutil.print_file_list(args) 276 elif options.ros_args: 277 import arg_dump as roslaunch_arg_dump 278 roslaunch_arg_dump.dump_args(args) 279 else: 280 node_args.print_node_list(args) 281 return 282 283 # we have to wait for the master here because we don't have the run_id yet 284 if options.wait_for_master: 285 if options.core: 286 parser.error("--wait cannot be used with roscore") 287 rlutil._wait_for_master() 288 289 # write the pid to a file 290 write_pid_file(options.pid_fn, options.core, options.port) 291 292 # spin up the logging infrastructure. have to wait until we can read options.run_id 293 uuid = rlutil.get_or_generate_uuid(options.run_id, options.wait_for_master) 294 configure_logging(uuid) 295 296 # #3088: don't check disk usage on remote machines 297 if not options.child_name and not options.skip_log_check: 298 # #2761 299 rlutil.check_log_disk_usage() 300 301 logger = logging.getLogger('roslaunch') 302 logger.info("roslaunch starting with args %s"%str(argv)) 303 logger.info("roslaunch env is %s"%os.environ) 304 305 if options.child_name: 306 logger.info('starting in child mode') 307 308 # This is a roslaunch child, spin up client server. 309 # client spins up an XML-RPC server that waits for 310 # commands and configuration from the server. 311 from . import child as roslaunch_child 312 c = roslaunch_child.ROSLaunchChild(uuid, options.child_name, options.server_uri, 313 sigint_timeout=options.sigint_timeout, 314 sigterm_timeout=options.sigterm_timeout) 315 c.run() 316 else: 317 logger.info('starting in server mode') 318 319 # #1491 change terminal name 320 if not options.disable_title: 321 rlutil.change_terminal_name(args, options.core) 322 323 # Read roslaunch string from stdin when - is passed as launch filename. 324 roslaunch_strs = [] 325 if '-' in args: 326 roslaunch_core.printlog("Passed '-' as file argument, attempting to read roslaunch XML from stdin.") 327 roslaunch_strs.append(sys.stdin.read()) 328 roslaunch_core.printlog("... %d bytes read successfully.\n" % len(roslaunch_strs[-1])) 329 args.remove('-') 330 331 # This is a roslaunch parent, spin up parent server and launch processes. 332 # args are the roslaunch files to load 333 from . import parent as roslaunch_parent 334 # force a port binding spec if we are running a core 335 if options.core: 336 options.port = options.port or DEFAULT_MASTER_PORT 337 p = roslaunch_parent.ROSLaunchParent(uuid, args, roslaunch_strs=roslaunch_strs, 338 is_core=options.core, port=options.port, local_only=options.local_only, 339 verbose=options.verbose, force_screen=options.force_screen, 340 force_log=options.force_log, 341 num_workers=options.num_workers, timeout=options.timeout, 342 master_logger_level=options.master_logger_level, 343 show_summary=not options.no_summary, 344 force_required=options.force_required, 345 sigint_timeout=options.sigint_timeout, 346 sigterm_timeout=options.sigterm_timeout) 347 p.start() 348 p.spin() 349 350 except RLException as e: 351 handle_exception(roslaunch_core, logger, "RLException: ", e) 352 except ValueError as e: 353 # TODO: need to trap better than this high-level trap 354 handle_exception(roslaunch_core, logger, "Value error: ", e) 355 except rospkg.ResourceNotFound as e: 356 handle_exception(roslaunch_core, logger, "Resource not found: ", e) 357 except Exception as e: 358 traceback.print_exc() 359 sys.exit(1) 360 finally: 361 # remove the pid file 362 if options is not None and options.pid_fn: 363 try: os.unlink(options.pid_fn) 364 except os.error: pass
365 366 367 if __name__ == '__main__': 368 main() 369