Package rosbag :: Module rosbag_main
[frames] | no frames]

Source Code for Module rosbag.rosbag_main

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2010, 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  from __future__ import print_function 
 34   
 35  import optparse 
 36  import os 
 37  import shutil 
 38  import signal 
 39  import subprocess 
 40  import sys 
 41  import time 
 42  try: 
 43      from UserDict import UserDict  # Python 2.x 
 44  except ImportError: 
 45      from collections import UserDict  # Python 3.x 
 46   
 47  import roslib.message 
 48  import roslib.packages 
 49   
 50  from .bag import Bag, Compression, ROSBagException, ROSBagFormatException, ROSBagUnindexedException 
 51  from .migration import MessageMigrator, fixbag2, checkbag 
 61   
62 -def handle_split(option, opt_str, value, parser):
63 parser.values.split = True 64 if len(parser.rargs) > 0 and parser.rargs[0].isdigit(): 65 print("Use of \"--split <MAX_SIZE>\" has been deprecated. Please use --split --size <MAX_SIZE> or --split --duration <MAX_DURATION>", file=sys.stderr) 66 parser.values.size = int(parser.rargs.pop(0))
67
68 69 -def _stop_process(signum, frame, old_handler, process):
70 process.terminate() 71 if old_handler: 72 old_handler(signum, frame)
73
74 75 -def record_cmd(argv):
76 parser = optparse.OptionParser(usage="rosbag record TOPIC1 [TOPIC2 TOPIC3 ...]", 77 description="Record a bag file with the contents of specified topics.", 78 formatter=optparse.IndentedHelpFormatter()) 79 80 parser.add_option("-a", "--all", dest="all", default=False, action="store_true", help="record all topics") 81 parser.add_option("-e", "--regex", dest="regex", default=False, action="store_true", help="match topics using regular expressions") 82 parser.add_option("-x", "--exclude", dest="exclude_regex", default="", action="store", help="exclude topics matching the follow regular expression (subtracts from -a or regex)") 83 parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true", help="suppress console output") 84 parser.add_option("-o", "--output-prefix", dest="prefix", default=None, action="store", help="prepend PREFIX to beginning of bag name (name will always end with date stamp)") 85 parser.add_option("-O", "--output-name", dest="name", default=None, action="store", help="record to bag with name NAME.bag") 86 parser.add_option( "--split", dest="split", default=False, callback=handle_split, action="callback", help="split the bag when maximum size or duration is reached") 87 parser.add_option( "--max-splits", dest="max_splits", type='int', action="store", help="Keep a maximum of N bag files, when reaching the maximum erase the oldest one to keep a constant number of files.", metavar="MAX_SPLITS") 88 parser.add_option( "--size", dest="size", type='int', action="store", help="record a bag of maximum size SIZE MB. (Default: infinite)", metavar="SIZE") 89 parser.add_option( "--duration", dest="duration", type='string',action="store", help="record a bag of maximum duration DURATION in seconds, unless 'm', or 'h' is appended.", metavar="DURATION") 90 parser.add_option("-b", "--buffsize", dest="buffsize", default=256, type='int', action="store", help="use an internal buffer of SIZE MB (Default: %default, 0 = infinite)", metavar="SIZE") 91 parser.add_option("--chunksize", dest="chunksize", default=768, type='int', action="store", help="Advanced. Record to chunks of SIZE KB (Default: %default)", metavar="SIZE") 92 parser.add_option("-l", "--limit", dest="num", default=0, type='int', action="store", help="only record NUM messages on each topic") 93 parser.add_option( "--node", dest="node", default=None, type='string',action="store", help="record all topics subscribed to by a specific node") 94 parser.add_option("-j", "--bz2", dest="compression", default=None, action="store_const", const='bz2', help="use BZ2 compression") 95 parser.add_option("--lz4", dest="compression", action="store_const", const='lz4', help="use LZ4 compression") 96 parser.add_option("--tcpnodelay", dest="tcpnodelay", action="store_true", help="Use the TCP_NODELAY transport hint when subscribing to topics.") 97 parser.add_option("--udp", dest="udp", action="store_true", help="Use the UDP transport hint when subscribing to topics.") 98 99 (options, args) = parser.parse_args(argv) 100 101 if len(args) == 0 and not options.all and not options.node: 102 parser.error("You must specify a topic name or else use the '-a' option.") 103 104 if options.prefix is not None and options.name is not None: 105 parser.error("Can't set both prefix and name.") 106 107 recordpath = roslib.packages.find_node('rosbag', 'record') 108 if not recordpath: 109 parser.error("Cannot find rosbag/record executable") 110 cmd = [recordpath[0]] 111 112 cmd.extend(['--buffsize', str(options.buffsize)]) 113 cmd.extend(['--chunksize', str(options.chunksize)]) 114 115 if options.num != 0: cmd.extend(['--limit', str(options.num)]) 116 if options.quiet: cmd.extend(["--quiet"]) 117 if options.prefix: cmd.extend(["-o", options.prefix]) 118 if options.name: cmd.extend(["-O", options.name]) 119 if options.exclude_regex: cmd.extend(["--exclude", options.exclude_regex]) 120 if options.all: cmd.extend(["--all"]) 121 if options.regex: cmd.extend(["--regex"]) 122 if options.compression: cmd.extend(["--%s" % options.compression]) 123 if options.split: 124 if not options.duration and not options.size: 125 parser.error("Split specified without giving a maximum duration or size") 126 cmd.extend(["--split"]) 127 if options.max_splits: 128 cmd.extend(["--max-splits", str(options.max_splits)]) 129 if options.duration: cmd.extend(["--duration", options.duration]) 130 if options.size: cmd.extend(["--size", str(options.size)]) 131 if options.node: 132 cmd.extend(["--node", options.node]) 133 if options.tcpnodelay: cmd.extend(["--tcpnodelay"]) 134 if options.udp: cmd.extend(["--udp"]) 135 136 cmd.extend(args) 137 138 old_handler = signal.signal( 139 signal.SIGTERM, 140 lambda signum, frame: _stop_process(signum, frame, old_handler, process) 141 ) 142 # Better way of handling it than os.execv 143 # This makes sure stdin handles are passed to the process. 144 process = subprocess.Popen(cmd) 145 process.wait()
146
147 148 -def info_cmd(argv):
149 parser = optparse.OptionParser(usage='rosbag info [options] BAGFILE1 [BAGFILE2 BAGFILE3 ...]', 150 description='Summarize the contents of one or more bag files.') 151 parser.add_option('-y', '--yaml', dest='yaml', default=False, action='store_true', help='print information in YAML format') 152 parser.add_option('-k', '--key', dest='key', default=None, action='store', help='print information on the given key') 153 parser.add_option( '--freq', dest='freq', default=False, action='store_true', help='display topic message frequency statistics') 154 (options, args) = parser.parse_args(argv) 155 156 if len(args) == 0: 157 parser.error('You must specify at least 1 bag file.') 158 if options.key and not options.yaml: 159 parser.error('You can only specify key when printing in YAML format.') 160 161 for i, arg in enumerate(args): 162 try: 163 b = Bag(arg, 'r', skip_index=not options.freq) 164 if options.yaml: 165 info = b._get_yaml_info(key=options.key) 166 if info is not None: 167 print(info) 168 else: 169 print(b) 170 b.close() 171 if i < len(args) - 1: 172 print('---') 173 174 except ROSBagUnindexedException as ex: 175 print('ERROR bag unindexed: %s. Run rosbag reindex.' % arg, 176 file=sys.stderr) 177 sys.exit(1) 178 except ROSBagException as ex: 179 print('ERROR reading %s: %s' % (arg, str(ex)), file=sys.stderr) 180 sys.exit(1) 181 except IOError as ex: 182 print('ERROR reading %s: %s' % (arg, str(ex)), file=sys.stderr) 183 sys.exit(1)
184
185 186 -def handle_topics(option, opt_str, value, parser):
187 topics = [] 188 for arg in parser.rargs: 189 if arg[:2] == "--" and len(arg) > 2: 190 break 191 if arg[:1] == "-" and len(arg) > 1: 192 break 193 topics.append(arg) 194 parser.values.topics.extend(topics) 195 del parser.rargs[:len(topics)]
196
197 198 -def handle_pause_topics(option, opt_str, value, parser):
199 pause_topics = [] 200 for arg in parser.rargs: 201 if arg[:2] == "--" and len(arg) > 2: 202 break 203 if arg[:1] == "-" and len(arg) > 1: 204 break 205 pause_topics.append(arg) 206 parser.values.pause_topics.extend(pause_topics) 207 del parser.rargs[:len(pause_topics)]
208
209 210 -def play_cmd(argv):
211 parser = optparse.OptionParser(usage="rosbag play BAGFILE1 [BAGFILE2 BAGFILE3 ...]", 212 description="Play back the contents of one or more bag files in a time-synchronized fashion.") 213 parser.add_option("-p", "--prefix", dest="prefix", default='', type='str', help="prefix all output topics") 214 parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true", help="suppress console output") 215 parser.add_option("-i", "--immediate", dest="immediate", default=False, action="store_true", help="play back all messages without waiting") 216 parser.add_option("--pause", dest="pause", default=False, action="store_true", help="start in paused mode") 217 parser.add_option("--queue", dest="queue", default=100, type='int', action="store", help="use an outgoing queue of size SIZE (defaults to %default)", metavar="SIZE") 218 parser.add_option("--clock", dest="clock", default=False, action="store_true", help="publish the clock time") 219 parser.add_option("--hz", dest="freq", default=100, type='float', action="store", help="use a frequency of HZ when publishing clock time (default: %default)", metavar="HZ") 220 parser.add_option("-d", "--delay", dest="delay", default=0.2, type='float', action="store", help="sleep SEC seconds after every advertise call (to allow subscribers to connect)", metavar="SEC") 221 parser.add_option("-r", "--rate", dest="rate", default=1.0, type='float', action="store", help="multiply the publish rate by FACTOR", metavar="FACTOR") 222 parser.add_option("-s", "--start", dest="start", default=0.0, type='float', action="store", help="start SEC seconds into the bag files", metavar="SEC") 223 parser.add_option("-u", "--duration", dest="duration", default=None, type='float', action="store", help="play only SEC seconds from the bag files", metavar="SEC") 224 parser.add_option("--skip-empty", dest="skip_empty", default=None, type='float', action="store", help="skip regions in the bag with no messages for more than SEC seconds", metavar="SEC") 225 parser.add_option("-l", "--loop", dest="loop", default=False, action="store_true", help="loop playback") 226 parser.add_option("-k", "--keep-alive", dest="keep_alive", default=False, action="store_true", help="keep alive past end of bag (useful for publishing latched topics)") 227 parser.add_option("--try-future-version", dest="try_future", default=False, action="store_true", help="still try to open a bag file, even if the version number is not known to the player") 228 parser.add_option("--topics", dest="topics", default=[], callback=handle_topics, action="callback", help="topics to play back") 229 parser.add_option("--pause-topics", dest="pause_topics", default=[], callback=handle_pause_topics, action="callback", help="topics to pause on during playback") 230 parser.add_option("--bags", help="bags files to play back from") 231 parser.add_option("--wait-for-subscribers", dest="wait_for_subscribers", default=False, action="store_true", help="wait for at least one subscriber on each topic before publishing") 232 parser.add_option("--rate-control-topic", dest="rate_control_topic", default='', type='str', help="watch the given topic, and if the last publish was more than <rate-control-max-delay> ago, wait until the topic publishes again to continue playback") 233 parser.add_option("--rate-control-max-delay", dest="rate_control_max_delay", default=1.0, type='float', help="maximum time difference from <rate-control-topic> before pausing") 234 235 (options, args) = parser.parse_args(argv) 236 237 if options.bags: 238 args.append(options.bags) 239 240 if len(args) == 0: 241 parser.error('You must specify at least 1 bag file to play back.') 242 243 playpath = roslib.packages.find_node('rosbag', 'play') 244 if not playpath: 245 parser.error("Cannot find rosbag/play executable") 246 cmd = [playpath[0]] 247 248 if options.prefix: 249 cmd.extend(["--prefix", str(options.prefix)]) 250 251 if options.quiet: cmd.extend(["--quiet"]) 252 if options.pause: cmd.extend(["--pause"]) 253 if options.immediate: cmd.extend(["--immediate"]) 254 if options.loop: cmd.extend(["--loop"]) 255 if options.keep_alive: cmd.extend(["--keep-alive"]) 256 if options.try_future: cmd.extend(["--try-future-version"]) 257 if options.wait_for_subscribers: cmd.extend(["--wait-for-subscribers"]) 258 259 if options.clock: 260 cmd.extend(["--clock", "--hz", str(options.freq)]) 261 262 cmd.extend(['--queue', str(options.queue)]) 263 cmd.extend(['--rate', str(options.rate)]) 264 cmd.extend(['--delay', str(options.delay)]) 265 cmd.extend(['--start', str(options.start)]) 266 if options.duration: 267 cmd.extend(['--duration', str(options.duration)]) 268 if options.skip_empty: 269 cmd.extend(['--skip-empty', str(options.skip_empty)]) 270 271 if options.topics: 272 cmd.extend(['--topics'] + options.topics) 273 274 if options.pause_topics: 275 cmd.extend(['--pause-topics'] + options.pause_topics) 276 277 # prevent bag files to be passed as --topics or --pause-topics 278 if options.topics or options.pause_topics: 279 cmd.extend(['--bags']) 280 281 cmd.extend(args) 282 283 if options.rate_control_topic: 284 cmd.extend(['--rate-control-topic', str(options.rate_control_topic)]) 285 286 if options.rate_control_max_delay: 287 cmd.extend(['--rate-control-max-delay', str(options.rate_control_max_delay)]) 288 289 old_handler = signal.signal( 290 signal.SIGTERM, 291 lambda signum, frame: _stop_process(signum, frame, old_handler, process) 292 ) 293 # Better way of handling it than os.execv 294 # This makes sure stdin handles are passed to the process. 295 process = subprocess.Popen(cmd) 296 process.wait()
297
298 299 -def filter_cmd(argv):
300 def expr_eval(expr): 301 def eval_fn(topic, m, t): 302 return eval(expr)
303 return eval_fn 304 305 parser = optparse.OptionParser(usage="""rosbag filter [options] INBAG OUTBAG EXPRESSION 306 307 EXPRESSION can be any Python-legal expression. 308 309 The following variables are available: 310 * topic: name of topic 311 * m: message 312 * t: time of message (t.secs, t.nsecs)""", 313 description='Filter the contents of the bag.') 314 parser.add_option('-p', '--print', action='store', dest='verbose_pattern', default=None, metavar='PRINT-EXPRESSION', help='Python expression to print for verbose debugging. Uses same variables as filter-expression') 315 316 options, args = parser.parse_args(argv) 317 if len(args) == 0: 318 parser.error('You must specify an in bag, an out bag, and an expression.') 319 if len(args) == 1: 320 parser.error('You must specify an out bag and an expression.') 321 if len(args) == 2: 322 parser.error("You must specify an expression.") 323 if len(args) > 3: 324 parser.error("Too many arguments.") 325 326 inbag_filename, outbag_filename, expr = args 327 328 if not os.path.isfile(inbag_filename): 329 print('Cannot locate input bag file [%s]' % inbag_filename, file=sys.stderr) 330 sys.exit(2) 331 332 if os.path.realpath(inbag_filename) == os.path.realpath(outbag_filename): 333 print('Cannot use same file as input and output [%s]' % inbag_filename, file=sys.stderr) 334 sys.exit(3) 335 336 filter_fn = expr_eval(expr) 337 338 outbag = Bag(outbag_filename, 'w') 339 340 try: 341 inbag = Bag(inbag_filename) 342 except ROSBagUnindexedException as ex: 343 print('ERROR bag unindexed: %s. Run rosbag reindex.' % inbag_filename, file=sys.stderr) 344 sys.exit(1) 345 346 try: 347 meter = ProgressMeter(outbag_filename, inbag._uncompressed_size) 348 total_bytes = 0 349 350 if options.verbose_pattern: 351 verbose_pattern = expr_eval(options.verbose_pattern) 352 353 for topic, raw_msg, t in inbag.read_messages(raw=True): 354 msg_type, serialized_bytes, md5sum, pos, pytype = raw_msg 355 msg = pytype() 356 msg.deserialize(serialized_bytes) 357 358 if filter_fn(topic, msg, t): 359 print('MATCH', verbose_pattern(topic, msg, t)) 360 outbag.write(topic, msg, t) 361 else: 362 print('NO MATCH', verbose_pattern(topic, msg, t)) 363 364 total_bytes += len(serialized_bytes) 365 meter.step(total_bytes) 366 else: 367 for topic, raw_msg, t in inbag.read_messages(raw=True): 368 msg_type, serialized_bytes, md5sum, pos, pytype = raw_msg 369 msg = pytype() 370 msg.deserialize(serialized_bytes) 371 372 if filter_fn(topic, msg, t): 373 outbag.write(topic, msg, t) 374 375 total_bytes += len(serialized_bytes) 376 meter.step(total_bytes) 377 378 meter.finish() 379 380 finally: 381 inbag.close() 382 outbag.close() 383
384 -def fix_cmd(argv):
385 parser = optparse.OptionParser(usage='rosbag fix INBAG OUTBAG [EXTRARULES1 EXTRARULES2 ...]', description='Repair the messages in a bag file so that it can be played in the current system.') 386 parser.add_option('-n', '--noplugins', action='store_true', dest='noplugins', help='do not load rulefiles via plugins') 387 parser.add_option('--force', action='store_true', dest='force', help='proceed with migrations, even if not all rules defined') 388 389 (options, args) = parser.parse_args(argv) 390 391 if len(args) < 1: 392 parser.error('You must pass input and output bag files.') 393 if len(args) < 2: 394 parser.error('You must pass an output bag file.') 395 396 inbag_filename = args[0] 397 outbag_filename = args[1] 398 rules = args[2:] 399 400 ext = os.path.splitext(outbag_filename)[1] 401 if ext == '.bmr': 402 parser.error('Input file should be a bag file, not a rule file.') 403 if ext != '.bag': 404 parser.error('Output file must be a bag file.') 405 406 outname = outbag_filename + '.tmp' 407 408 if os.path.exists(outbag_filename): 409 if not os.access(outbag_filename, os.W_OK): 410 print('Don\'t have permissions to access %s' % outbag_filename, file=sys.stderr) 411 sys.exit(1) 412 else: 413 try: 414 file = open(outbag_filename, 'w') 415 file.close() 416 except IOError as e: 417 print('Cannot open %s for writing' % outbag_filename, file=sys.stderr) 418 sys.exit(1) 419 420 if os.path.exists(outname): 421 if not os.access(outname, os.W_OK): 422 print('Don\'t have permissions to access %s' % outname, file=sys.stderr) 423 sys.exit(1) 424 else: 425 try: 426 file = open(outname, 'w') 427 file.close() 428 except IOError as e: 429 print('Cannot open %s for writing' % outname, file=sys.stderr) 430 sys.exit(1) 431 432 if options.noplugins is None: 433 options.noplugins = False 434 435 migrator = MessageMigrator(rules, plugins=not options.noplugins) 436 437 try: 438 migrations = fixbag2(migrator, inbag_filename, outname, options.force) 439 except ROSBagUnindexedException as ex: 440 print('ERROR bag unindexed: %s. Run rosbag reindex.' % inbag_filename, 441 file=sys.stderr) 442 sys.exit(1) 443 444 if len(migrations) == 0: 445 os.rename(outname, outbag_filename) 446 print('Bag migrated successfully.') 447 else: 448 print('Bag could not be migrated. The following migrations could not be performed:') 449 for m in migrations: 450 print_trans(m[0][0].old_class, m[0][-1].new_class, 0) 451 452 if len(m[1]) > 0: 453 print(' %d rules missing:' % len(m[1])) 454 for r in m[1]: 455 print_trans(r.old_class, r.new_class,1) 456 457 print('Try running \'rosbag check\' to create the necessary rule files or run \'rosbag fix\' with the \'--force\' option.') 458 os.remove(outname) 459 sys.exit(1)
460
461 -def check_cmd(argv):
462 parser = optparse.OptionParser(usage='rosbag check BAG [-g RULEFILE] [EXTRARULES1 EXTRARULES2 ...]', description='Determine whether a bag is playable in the current system, or if it can be migrated.') 463 parser.add_option('-g', '--genrules', action='store', dest='rulefile', default=None, help='generate a rulefile named RULEFILE') 464 parser.add_option('-a', '--append', action='store_true', dest='append', help='append to the end of an existing rulefile after loading it') 465 parser.add_option('-n', '--noplugins', action='store_true', dest='noplugins', help='do not load rulefiles via plugins') 466 (options, args) = parser.parse_args(argv) 467 468 if len(args) == 0: 469 parser.error('You must specify a bag file to check.') 470 if options.append and options.rulefile is None: 471 parser.error('Cannot specify -a without also specifying -g.') 472 if options.rulefile is not None: 473 rulefile_exists = os.path.isfile(options.rulefile) 474 if rulefile_exists and not options.append: 475 parser.error('The file %s already exists. Include -a if you intend to append.' % options.rulefile) 476 if not rulefile_exists and options.append: 477 parser.error('The file %s does not exist, and so -a is invalid.' % options.rulefile) 478 479 if options.append: 480 append_rule = [options.rulefile] 481 else: 482 append_rule = [] 483 484 # First check that the bag is not unindexed 485 try: 486 Bag(args[0]) 487 except ROSBagUnindexedException as ex: 488 print('ERROR bag unindexed: %s. Run rosbag reindex.' % args[0], file=sys.stderr) 489 sys.exit(1) 490 491 mm = MessageMigrator(args[1:] + append_rule, not options.noplugins) 492 493 migrations = checkbag(mm, args[0]) 494 495 if len(migrations) == 0: 496 print('Bag file does not need any migrations.') 497 exit(0) 498 499 print('The following migrations need to occur:') 500 all_rules = [] 501 for m in migrations: 502 all_rules.extend(m[1]) 503 504 print_trans(m[0][0].old_class, m[0][-1].new_class, 0) 505 if len(m[1]) > 0: 506 print(" %d rules missing:" % len(m[1])) 507 for r in m[1]: 508 print_trans(r.old_class, r.new_class, 1) 509 510 if options.rulefile is None: 511 if all_rules == []: 512 print("\nAll rules defined. Bag is ready to be migrated") 513 else: 514 print("\nTo generate rules, please run with -g <rulefile>") 515 exit(0) 516 517 output = '' 518 rules_left = mm.filter_rules_unique(all_rules) 519 520 if rules_left == []: 521 print("\nNo additional rule files needed to be generated. %s not created."%(options.rulefile)) 522 exit(0) 523 524 while len(rules_left) > 0: 525 extra_rules = [] 526 for r in rules_left: 527 if r.new_class is None: 528 print('The message type %s appears to have moved. Please enter the type to migrate it to.' % r.old_class._type) 529 new_type = raw_input('>') 530 new_class = roslib.message.get_message_class(new_type) 531 while new_class is None: 532 print("\'%s\' could not be found in your system. Please make sure it is built." % new_type) 533 new_type = raw_input('>') 534 new_class = roslib.message.get_message_class(new_type) 535 new_rule = mm.make_update_rule(r.old_class, new_class) 536 R = new_rule(mm, 'GENERATED.' + new_rule.__name__) 537 R.find_sub_paths() 538 new_rules = [r for r in mm.expand_rules(R.sub_rules) if r.valid == False] 539 extra_rules.extend(new_rules) 540 print('Creating the migration rule for %s requires additional missing rules:' % new_type) 541 for nr in new_rules: 542 print_trans(nr.old_class, nr.new_class,1) 543 output += R.get_class_def() 544 else: 545 output += r.get_class_def() 546 rules_left = mm.filter_rules_unique(extra_rules) 547 f = open(options.rulefile, 'a') 548 f.write(output) 549 f.close() 550 551 print('\nThe necessary rule files have been written to: %s' % options.rulefile)
552
553 -def compress_cmd(argv):
554 parser = optparse.OptionParser(usage='rosbag compress [options] BAGFILE1 [BAGFILE2 ...]', 555 description='Compress one or more bag files.') 556 parser.add_option( '--output-dir', action='store', dest='output_dir', help='write to directory DIR', metavar='DIR') 557 parser.add_option('-f', '--force', action='store_true', dest='force', help='force overwriting of backup file if it exists') 558 parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='suppress noncritical messages') 559 parser.add_option('-j', '--bz2', action='store_const', dest='compression', help='use BZ2 compression', const=Compression.BZ2, default=Compression.BZ2) 560 parser.add_option( '--lz4', action='store_const', dest='compression', help='use lz4 compression', const=Compression.LZ4) 561 (options, args) = parser.parse_args(argv) 562 563 if len(args) < 1: 564 parser.error('You must specify at least one bag file.') 565 566 op = lambda inbag, outbag, quiet: change_compression_op(inbag, outbag, options.compression, options.quiet) 567 568 bag_op(args, False, lambda b: False, op, options.output_dir, options.force, options.quiet)
569
570 -def decompress_cmd(argv):
571 parser = optparse.OptionParser(usage='rosbag decompress [options] BAGFILE1 [BAGFILE2 ...]', 572 description='Decompress one or more bag files.') 573 parser.add_option( '--output-dir', action='store', dest='output_dir', help='write to directory DIR', metavar='DIR') 574 parser.add_option('-f', '--force', action='store_true', dest='force', help='force overwriting of backup file if it exists') 575 parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='suppress noncritical messages') 576 577 (options, args) = parser.parse_args(argv) 578 579 if len(args) < 1: 580 parser.error('You must specify at least one bag file.') 581 582 op = lambda inbag, outbag, quiet: change_compression_op(inbag, outbag, Compression.NONE, options.quiet) 583 584 bag_op(args, False, lambda b: False, op, options.output_dir, options.force, options.quiet)
585
586 -def reindex_cmd(argv):
587 parser = optparse.OptionParser(usage='rosbag reindex [options] BAGFILE1 [BAGFILE2 ...]', 588 description='Reindexes one or more bag files.') 589 parser.add_option( '--output-dir', action='store', dest='output_dir', help='write to directory DIR', metavar='DIR') 590 parser.add_option('-f', '--force', action='store_true', dest='force', help='force overwriting of backup file if it exists') 591 parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='suppress noncritical messages') 592 593 (options, args) = parser.parse_args(argv) 594 595 if len(args) < 1: 596 parser.error('You must specify at least one bag file.') 597 598 op = lambda inbag, outbag, quiet: reindex_op(inbag, outbag, options.quiet) 599 600 bag_op(args, True, lambda b: b.version > 102, op, options.output_dir, options.force, options.quiet)
601
602 -def bag_op(inbag_filenames, allow_unindexed, copy_fn, op, output_dir=None, force=False, quiet=False):
603 for inbag_filename in inbag_filenames: 604 # Check we can read the file 605 try: 606 inbag = Bag(inbag_filename, 'r', allow_unindexed=allow_unindexed) 607 except ROSBagUnindexedException: 608 print('ERROR bag unindexed: %s. Run rosbag reindex.' % inbag_filename, file=sys.stderr) 609 continue 610 except (ROSBagException, IOError) as ex: 611 print('ERROR reading %s: %s' % (inbag_filename, str(ex)), file=sys.stderr) 612 continue 613 614 # Determine whether we should copy the bag 615 copy = copy_fn(inbag) 616 617 inbag.close() 618 619 # Determine filename for output bag 620 if output_dir is None: 621 outbag_filename = inbag_filename 622 else: 623 outbag_filename = os.path.join(output_dir, os.path.split(inbag_filename)[1]) 624 625 backup_filename = None 626 if outbag_filename == inbag_filename: 627 # Rename the input bag to ###.orig.###, and open for reading 628 backup_filename = '%s.orig%s' % os.path.splitext(inbag_filename) 629 630 if not force and os.path.exists(backup_filename): 631 if not quiet: 632 print('Skipping %s. Backup path %s already exists.' % (inbag_filename, backup_filename), file=sys.stderr) 633 continue 634 635 try: 636 if copy: 637 shutil.copy(inbag_filename, backup_filename) 638 else: 639 os.rename(inbag_filename, backup_filename) 640 except OSError as ex: 641 print('ERROR %s %s to %s: %s' % ('copying' if copy else 'moving', inbag_filename, backup_filename, str(ex)), file=sys.stderr) 642 continue 643 644 source_filename = backup_filename 645 else: 646 if copy: 647 shutil.copy(inbag_filename, outbag_filename) 648 source_filename = outbag_filename 649 else: 650 source_filename = inbag_filename 651 652 try: 653 inbag = Bag(source_filename, 'r', allow_unindexed=allow_unindexed) 654 655 # Open the output bag file for writing 656 try: 657 if copy: 658 outbag = Bag(outbag_filename, 'a', allow_unindexed=allow_unindexed) 659 else: 660 outbag = Bag(outbag_filename, 'w') 661 except (ROSBagException, IOError) as ex: 662 print('ERROR writing to %s: %s' % (outbag_filename, str(ex)), file=sys.stderr) 663 inbag.close() 664 continue 665 666 # Perform the operation 667 try: 668 op(inbag, outbag, quiet=quiet) 669 except ROSBagException as ex: 670 print('\nERROR operating on %s: %s' % (source_filename, str(ex)), file=sys.stderr) 671 inbag.close() 672 outbag.close() 673 continue 674 675 outbag.close() 676 inbag.close() 677 678 except KeyboardInterrupt: 679 if backup_filename is not None: 680 try: 681 if copy: 682 os.remove(backup_filename) 683 else: 684 os.rename(backup_filename, inbag_filename) 685 except OSError as ex: 686 print('ERROR %s %s to %s: %s', ('removing' if copy else 'moving', backup_filename, inbag_filename, str(ex)), file=sys.stderr) 687 break 688 689 except (ROSBagException, IOError) as ex: 690 print('ERROR operating on %s: %s' % (inbag_filename, str(ex)), file=sys.stderr)
691
692 -def change_compression_op(inbag, outbag, compression, quiet):
693 outbag.compression = compression 694 695 if quiet: 696 for topic, msg, t in inbag.read_messages(raw=True): 697 outbag.write(topic, msg, t, raw=True) 698 else: 699 meter = ProgressMeter(outbag.filename, inbag._uncompressed_size) 700 701 total_bytes = 0 702 for topic, msg, t in inbag.read_messages(raw=True): 703 msg_type, serialized_bytes, md5sum, pos, pytype = msg 704 705 outbag.write(topic, msg, t, raw=True) 706 707 total_bytes += len(serialized_bytes) 708 meter.step(total_bytes) 709 710 meter.finish()
711
712 -def reindex_op(inbag, outbag, quiet):
713 if inbag.version == 102: 714 if quiet: 715 try: 716 for offset in inbag.reindex(): 717 pass 718 except: 719 pass 720 721 for (topic, msg, t) in inbag.read_messages(): 722 outbag.write(topic, msg, t) 723 else: 724 meter = ProgressMeter(outbag.filename, inbag.size) 725 try: 726 for offset in inbag.reindex(): 727 meter.step(offset) 728 except: 729 pass 730 meter.finish() 731 732 meter = ProgressMeter(outbag.filename, inbag.size) 733 for (topic, msg, t) in inbag.read_messages(): 734 outbag.write(topic, msg, t) 735 meter.step(inbag._file.tell()) 736 meter.finish() 737 else: 738 if quiet: 739 try: 740 for offset in outbag.reindex(): 741 pass 742 except: 743 pass 744 else: 745 meter = ProgressMeter(outbag.filename, outbag.size) 746 try: 747 for offset in outbag.reindex(): 748 meter.step(offset) 749 except: 750 pass 751 meter.finish()
752
753 -class RosbagCmds(UserDict):
754 - def __init__(self):
755 UserDict.__init__(self) 756 self._description = {} 757 self['help'] = self.help_cmd
758
759 - def add_cmd(self, name, function, description):
760 self[name] = function 761 self._description[name] = description
762
763 - def get_valid_cmds(self):
764 str = "Available subcommands:\n" 765 for k in sorted(self.keys()): 766 str += " %s " % k 767 if k in self._description.keys(): 768 str +="\t%s" % self._description[k] 769 str += "\n" 770 return str
771
772 - def help_cmd(self,argv):
773 argv = [a for a in argv if a != '-h' and a != '--help'] 774 775 if len(argv) == 0: 776 print('Usage: rosbag <subcommand> [options] [args]') 777 print() 778 print("A bag is a file format in ROS for storing ROS message data. The rosbag command can record, replay and manipulate bags.") 779 print() 780 print(self.get_valid_cmds()) 781 print('For additional information, see http://wiki.ros.org/rosbag') 782 print() 783 return 784 785 cmd = argv[0] 786 if cmd in self: 787 self[cmd](['-h']) 788 else: 789 print("Unknown command: '%s'" % cmd, file=sys.stderr) 790 print(self.get_valid_cmds(), file=sys.stderr)
791
792 -class ProgressMeter(object):
793 - def __init__(self, path, bytes_total, refresh_rate=1.0):
794 self.path = path 795 self.bytes_total = bytes_total 796 self.refresh_rate = refresh_rate 797 798 self.elapsed = 0.0 799 self.update_elapsed = 0.0 800 self.bytes_read = 0.0 801 802 self.start_time = time.time() 803 804 self._update_progress()
805
806 - def step(self, bytes_read, force_update=False):
807 self.bytes_read = bytes_read 808 self.elapsed = time.time() - self.start_time 809 810 if force_update or self.elapsed - self.update_elapsed > self.refresh_rate: 811 self._update_progress() 812 self.update_elapsed = self.elapsed
813
814 - def _update_progress(self):
815 max_path_len = self.terminal_width() - 37 816 path = self.path 817 if len(path) > max_path_len: 818 path = '...' + self.path[-max_path_len + 3:] 819 820 bytes_read_str = self._human_readable_size(float(self.bytes_read)) 821 bytes_total_str = self._human_readable_size(float(self.bytes_total)) 822 823 if self.bytes_read < self.bytes_total: 824 complete_fraction = float(self.bytes_read) / self.bytes_total 825 pct_complete = int(100.0 * complete_fraction) 826 827 if complete_fraction > 0.0: 828 eta = self.elapsed * (1.0 / complete_fraction - 1.0) 829 eta_min, eta_sec = eta / 60, eta % 60 830 if eta_min > 99: 831 eta_str = '--:--' 832 else: 833 eta_str = '%02d:%02d' % (eta_min, eta_sec) 834 else: 835 eta_str = '--:--' 836 837 progress = '%-*s %3d%% %8s / %8s %s ETA' % (max_path_len, path, pct_complete, bytes_read_str, bytes_total_str, eta_str) 838 else: 839 progress = '%-*s 100%% %19s %02d:%02d ' % (max_path_len, path, bytes_total_str, self.elapsed / 60, self.elapsed % 60) 840 841 print('\r', progress, end='') 842 sys.stdout.flush()
843
844 - def _human_readable_size(self, size):
845 multiple = 1024.0 846 for suffix in ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']: 847 size /= multiple 848 if size < multiple: 849 return '%.1f %s' % (size, suffix) 850 851 raise ValueError('number too large')
852
853 - def finish(self):
854 self.step(self.bytes_total, force_update=True) 855 print()
856 857 @staticmethod
858 - def terminal_width():
859 """Estimate the width of the terminal""" 860 width = 0 861 try: 862 import struct, fcntl, termios 863 s = struct.pack('HHHH', 0, 0, 0, 0) 864 x = fcntl.ioctl(1, termios.TIOCGWINSZ, s) 865 width = struct.unpack('HHHH', x)[1] 866 except (IOError, ImportError): 867 pass 868 if width <= 0: 869 try: 870 width = int(os.environ['COLUMNS']) 871 except: 872 pass 873 if width <= 0: 874 width = 80 875 876 return width
877
878 -def rosbagmain(argv=None):
879 cmds = RosbagCmds() 880 cmds.add_cmd('record', record_cmd, "Record a bag file with the contents of specified topics.") 881 cmds.add_cmd('info', info_cmd, 'Summarize the contents of one or more bag files.') 882 cmds.add_cmd('play', play_cmd, "Play back the contents of one or more bag files in a time-synchronized fashion.") 883 cmds.add_cmd('check', check_cmd, 'Determine whether a bag is playable in the current system, or if it can be migrated.') 884 cmds.add_cmd('fix', fix_cmd, 'Repair the messages in a bag file so that it can be played in the current system.') 885 cmds.add_cmd('filter', filter_cmd, 'Filter the contents of the bag.') 886 cmds.add_cmd('compress', compress_cmd, 'Compress one or more bag files.') 887 cmds.add_cmd('decompress', decompress_cmd, 'Decompress one or more bag files.') 888 cmds.add_cmd('reindex', reindex_cmd, 'Reindexes one or more bag files.') 889 890 if argv is None: 891 argv = sys.argv 892 893 if '-h' in argv or '--help' in argv: 894 argv = [a for a in argv if a != '-h' and a != '--help'] 895 argv.insert(1, 'help') 896 897 if len(argv) > 1: 898 cmd = argv[1] 899 else: 900 cmd = 'help' 901 902 try: 903 if cmd in cmds: 904 cmds[cmd](argv[2:]) 905 else: 906 cmds['help']([cmd]) 907 except KeyboardInterrupt: 908 pass
909