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