Skip to content

Commit ab3adb0

Browse files
committed
cras_bag_tools: filter_bag: Added extra_bags option to pass additional bag files.
1 parent d75a75a commit ab3adb0

File tree

1 file changed

+52
-17
lines changed

1 file changed

+52
-17
lines changed

cras_bag_tools/scripts/filter_bag

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from shutil import copyfile
2222
import genpy
2323
import rosbag
2424
from cras import pretty_file_size
25+
from cras.string_utils import STRING_TYPE
2526

2627
from cras_bag_tools.bag_filter import filter_bag
2728
from cras_bag_tools.message_filter import FilterChain, MessageFilter, Passthrough, get_filters
@@ -94,17 +95,21 @@ def copy_params_if_any(param_file, out_bag_path):
9495

9596

9697
def filter_bags(bags, out_format, compression, copy_params, filter, default_params_file=None,
97-
start_time=None, end_time=None, time_ranges=None, limit_to_first_bag=False):
98+
start_time=None, end_time=None, time_ranges=None, limit_to_first_bag=False, extra_bags=None):
9899
"""Filter all given bags using the given filter.
99100
100101
:param list bags: The bags to filter. If multiple bags should be read at once, add them as a single list item with
101102
paths separated by colon. One of the files can also be be a YAML file with ROS parameters.
102-
:param str out_format: Output path template.
103+
All sub-bags except the first one can have their name formatted similar to `out_format`.
104+
:param str out_format: Output path template in the format :meth:`str.format()` accepts. Available variables are:
105+
`dirname`, `basename`, `name`, `ext`, `ext_no_dot`, `bag_prefix`, `bag_stamp`, `bag_suffix`,
106+
`bag_base`. See :meth:`resolve_path()` for details.
103107
:param str compression: Output bag compression. One of 'rosbag.Compression.*' constants.
104108
:param bool copy_params: If True, copy parameters file along with the bag file if it exists.
105109
:param MessageFilter filter: The filter to apply.
106110
:param str default_params_file: If nonempty, specifies the YAML file with ROS parameters that is used if no param
107-
file is specified for the particular bag.
111+
file is specified for the particular bag. The path will be resolved similar to
112+
out_format with the processed bag as reference file.
108113
:param genpy.Time start_time: Time from which the bag filtering should be started.
109114
:param genpy.Time end_time: Time to which the bag filtering should be stopped.
110115
:param TimeRanges time_ranges: Time ranges of the bag files to process. If start_time and end_time are specified,
@@ -113,27 +118,40 @@ def filter_bags(bags, out_format, compression, copy_params, filter, default_para
113118
:param bool limit_to_first_bag: If True, each multibag will report its start and end to be equal to the
114119
first open bag. If False, the start and end correspond to the earliest and latest
115120
stamp in all bags.
121+
:param extra_bags: List of bag files that will be added to each of the processed bags. Names of these bags
122+
will be resolved similar to out_format with the processed bag as reference file.
123+
:type extra_bags: list or str
116124
:return: The number of bags that failed to be processed.
117125
:rtype: int
118126
"""
119127
num_failed = 0
120128
i = 0
121129
for bags_path in bags:
122130
i += 1
123-
bag_path = bags_path
131+
bag_path = os.path.abspath(os.path.expanduser(bags_path))
124132
bags_paths = [bag_path]
125133
if os.path.pathsep in bags_path:
126134
bags_paths = bags_path.split(os.path.pathsep)
127-
bag_path = bags_paths[0]
135+
bag_path = os.path.abspath(os.path.expanduser(bags_paths[0]))
128136

129-
params_file = default_params_file
137+
for b in range(1, len(bags_paths)):
138+
bags_paths[b] = resolve_path(bags_paths[b], bag_path)
139+
required_bags = set(bags_paths)
140+
141+
if extra_bags is not None:
142+
if isinstance(extra_bags, STRING_TYPE):
143+
extra = extra_bags.split(os.path.pathsep)
144+
else:
145+
extra = list(extra_bags)
146+
bags_paths += [resolve_path(b, bag_path) for b in extra]
147+
148+
params_file = resolve_path(default_params_file, bag_path) if default_params_file is not None else None
130149
if params_file is None:
131150
for b in bags_paths:
132151
ext = os.path.splitext(b)[-1]
133152
if ext in ('.params', '.yaml', '.yml'):
134153
params_file = b
135154
bags_paths.remove(b)
136-
bags_path = os.path.pathsep.join(bags_paths)
137155
break
138156

139157
if params_file is None:
@@ -146,18 +164,24 @@ def filter_bags(bags, out_format, compression, copy_params, filter, default_para
146164
print()
147165
print("[{}/{}] Bag {}".format(i, len(bags), bag_path))
148166

149-
print('Source: %s' % (",".join([os.path.abspath(b) for b in bags_paths]),))
150167
bags_ok = True
168+
bags_to_remove = []
151169
for b in bags_paths:
152170
if not os.path.exists(b):
153-
print('Source bag %s does not exist' % (b,), file=sys.stderr)
154-
bags_ok = False
171+
if b in required_bags:
172+
print('Source bag %s does not exist' % (b,), file=sys.stderr)
173+
bags_ok = False
174+
bags_to_remove.append(b)
155175
if not bags_ok:
156176
num_failed += 1
157177
continue
178+
for b in bags_to_remove:
179+
bags_paths.remove(b)
180+
181+
print('Source: %s' % ("\n ".join([os.path.abspath(b) for b in bags_paths]),))
158182

159183
try:
160-
with TqdmMultiBag(bags_path, skip_index=True, limit_to_first_bag=limit_to_first_bag) as bag:
184+
with TqdmMultiBag(bags_paths, skip_index=True, limit_to_first_bag=limit_to_first_bag) as bag:
161185
print('- Size: %s' % (pretty_file_size(bag.size),))
162186

163187
out_bag_path = resolve_path(out_format, bag_path)
@@ -208,7 +232,7 @@ def filter_bags(bags, out_format, compression, copy_params, filter, default_para
208232
file=sys.stderr)
209233

210234
except Exception as e:
211-
print('Error processing bag file %s: %s' % (bags_path, str(e)), file=sys.stderr)
235+
print('Error processing bag file %s: %s' % (bag_path, str(e)), file=sys.stderr)
212236
num_failed += 1
213237
import traceback
214238
traceback.print_exc()
@@ -228,10 +252,19 @@ class TimeRangesAction(argparse.Action):
228252

229253
def main():
230254
parser = ArgumentParser()
231-
parser.add_argument('bags', nargs='*', help="The (multi)bag files to filter.")
255+
parser.add_argument('bags', nargs='*', help="The (multi)bag files to filter. A multibag is a colon-separated list "
256+
"of bag files that will be processed together (basically merged). "
257+
"The multibag can also specify at most one YAML file with parameters."
258+
"Except the first item, the names of all sub-bags in a multibag are "
259+
"treated as templates for str.format().")
260+
parser.add_argument('--extra-bags', dest='extra_bags', nargs='+', default=None,
261+
help="Bag files added to every (multi)bag from `bags`. If an extra bag doesn't exist, "
262+
"it will be ignored. The names of all extra bags are treated as templates for "
263+
"str.format().")
232264
parser.add_argument('-c', '--config', nargs='+', help="YAML configs of filters")
233265
parser.add_argument('-o', '--out-format', default=argparse.SUPPRESS,
234-
help='Template for naming the output bag. Defaults to "{name}.proc{ext}"')
266+
help='File name of the output bag. It is treated as a template for str.format(). '
267+
'Defaults to "{name}.proc{ext}".')
235268
parser.add_argument('--lz4', dest='compression', action='store_const', const=rosbag.Compression.LZ4,
236269
help="Compress the bag via LZ4")
237270
parser.add_argument('--bz2', dest='compression', action='store_const', const=rosbag.Compression.BZ2,
@@ -241,7 +274,8 @@ def main():
241274
help="If set, no .params file will be copied.")
242275
parser.add_argument('--default-params-file', dest='default_params_file', type=str, default=None,
243276
help="If nonempty, specifies the YAML file with ROS parameters that is used if no param file "
244-
"is specified for the particular bag.")
277+
"is specified for the particular bag. The file name is treated as a template for "
278+
"str.format().")
245279
parser.add_argument("--list-yaml-keys", dest="list_yaml_keys", action="store_true",
246280
help="Print a list of all available YAML top-level keys provided by filters.")
247281
parser.add_argument("--list-filters", dest="list_filters", action="store_true",
@@ -262,7 +296,8 @@ def main():
262296
getattr(f, 'add_cli_args')(parser)
263297

264298
default_yaml_keys = [
265-
'bags', 'out_format', 'compression', 'filters', 'copy_params', 'start_time', 'end_time', 'time_ranges',
299+
'bags', 'extra_bags', 'out_format', 'compression', 'filters', 'copy_params', 'start_time', 'end_time',
300+
'time_ranges',
266301
]
267302

268303
def default_process_cli_args(filters, args):
@@ -349,7 +384,7 @@ def main():
349384
args.bags, args.out_format, args.compression, args.copy_params, filter_chain, args.default_params_file,
350385
genpy.Time(args.start_time) if args.start_time is not None else None,
351386
genpy.Time(args.end_time) if args.end_time is not None else None, args.time_ranges,
352-
args.limit_to_first_bag)
387+
args.limit_to_first_bag, args.extra_bags)
353388

354389
sys.exit(num_failed)
355390

0 commit comments

Comments
 (0)