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