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 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
70 process.terminate()
71 if old_handler:
72 old_handler(signum, frame)
73
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
143
144 process = subprocess.Popen(cmd)
145 process.wait()
146
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
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
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
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
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
294
295 process = subprocess.Popen(cmd)
296 process.wait()
297
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
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
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
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
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
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
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
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
615 copy = copy_fn(inbag)
616
617 inbag.close()
618
619
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
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
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
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
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
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
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
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
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
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
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
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
854 self.step(self.bytes_total, force_update=True)
855 print()
856
857 @staticmethod
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