#!/usr/bin/env python3
import sys
import argparse
import multiprocessing
import rerun as rr
import rerun . blueprint as rrb
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 "
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 ( ) :
blueprint = None
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 ) )
@rr . shutdown_at_exit
def process ( blueprint , lr ) :
rr . init ( " rerun_test " )
rr . connect ( default_blueprint = blueprint )
ret = [ ]
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 ( " 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 ( )
blueprint = createBlueprint ( )
rr . init ( " rerun_test " )
rr . spawn ( connect = False ) # child processes stream data to Viewer
route_or_segment_name = DEMO_ROUTE if args . demo else args . route_or_segment_name . strip ( )
print ( " Getting route log paths " )
lr = LogReader ( route_or_segment_name )
lr . run_across_segments ( NUM_CPUS , partial ( process , blueprint ) )