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