diff --git a/tools/rerun/run.py b/tools/rerun/run.py new file mode 100755 index 0000000000..271f127d33 --- /dev/null +++ b/tools/rerun/run.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +import subprocess +import sys +import argparse +import multiprocessing +from functools import partial + +from openpilot.tools.lib.logreader import LogReader +from cereal.services import SERVICE_LIST + + +NUM_CPUS = multiprocessing.cpu_count() +DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" +WHEEL_URL = "https://build.rerun.io/commit/660463d/wheels" + + +def install(): + # currently requires a preview release build + subprocess.run([sys.executable, "-m", "pip", "install", "--pre", "-f", WHEEL_URL, "--upgrade", "rerun-sdk"], check=True) + print("Rerun installed") + + +def log_msg(msg, parent_key=''): + stack = [(msg, parent_key)] + while stack: + current_msg, current_parent_key = stack.pop() + if isinstance(current_msg, list): + for index, item in enumerate(current_msg): + new_key = f"{current_parent_key}/{index}" + if isinstance(item, (int, float)): + rr.log(str(new_key), rr.Scalar(item)) + elif isinstance(item, dict): + stack.append((item, new_key)) + elif isinstance(current_msg, dict): + for key, value in current_msg.items(): + new_key = f"{current_parent_key}/{key}" + if isinstance(value, (int, float)): + rr.log(str(new_key), rr.Scalar(value)) + elif isinstance(value, dict): + stack.append((value, new_key)) + elif isinstance(value, list): + for index, item in enumerate(value): + if isinstance(item, (int, float)): + rr.log(f"{new_key}/{index}", rr.Scalar(item)) + else: + pass # Not a plottable value + + +def createBlueprint(): + timeSeriesViews = [] + for topic in sorted(SERVICE_LIST.keys()): + timeSeriesViews.append(rrb.TimeSeriesView(name=topic, origin=f"/{topic}/", visible=False)) + rr.log(topic, rr.SeriesLine(name=topic), timeless=True) + blueprint = rrb.Blueprint(rrb.Grid(rrb.Vertical(*timeSeriesViews,rrb.SelectionPanel(expanded=False),rrb.TimePanel(expanded=False)), + rrb.Spatial2DView(name="thumbnail", origin="/thumbnail"))) + return blueprint + + +def log_thumbnail(thumbnailMsg): + bytesImgData = thumbnailMsg.get('thumbnail') + rr.log("/thumbnail", rr.ImageEncoded(contents=bytesImgData)) + + +def process(blueprint, lr): + ret = [] + rr.init("rerun_test", spawn=True, default_blueprint=blueprint) + for msg in lr: + ret.append(msg) + rr.set_time_nanos("TIMELINE", msg.logMonoTime) + if msg.which() != "thumbnail": + log_msg(msg.to_dict()[msg.which()], msg.which()) + else: + log_thumbnail(msg.to_dict()[msg.which()]) + return ret + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="A helper to run rerun on openpilot routes", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one") + parser.add_argument("--install", action="store_true", help="Install or update rerun") + parser.add_argument("route_or_segment_name", nargs='?', help="The route or segment name to plot") + + if len(sys.argv) == 1: + parser.print_help() + sys.exit() + + args = parser.parse_args() + if args.install: + install() + sys.exit() + + try: + import rerun as rr + import rerun.blueprint as rrb + except ImportError: + print("Rerun is not installed, run with --install first") + sys.exit() + + route_or_segment_name = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip() + blueprint = createBlueprint() + print("Getting route log paths") + lr = LogReader(route_or_segment_name) + lr.run_across_segments(NUM_CPUS, partial(process, blueprint))