#!/usr/bin/env python3 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  os 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  sys 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  bz2 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  urllib . parse 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  capnp 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  warnings 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  cereal  import  log  as  capnp_log 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  tools . lib . filereader  import  FileReader 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  tools . lib . route  import  Route ,  SegmentName 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								# this is an iterator itself, and uses private variables from LogReader 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								class  MultiLogIterator : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  __init__ ( self ,  log_paths ,  sort_by_time = False ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _log_paths  =  log_paths 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    self . sort_by_time  =  sort_by_time 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _first_log_idx  =  next ( i  for  i  in  range ( len ( log_paths ) )  if  log_paths [ i ]  is  not  None ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _current_log  =  self . _first_log_idx 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _idx  =  0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _log_readers  =  [ None ] * len ( log_paths ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . start_time  =  self . _log_reader ( self . _first_log_idx ) . _ts [ 0 ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  _log_reader ( self ,  i ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  self . _log_readers [ i ]  is  None  and  self . _log_paths [ i ]  is  not  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      log_path  =  self . _log_paths [ i ] 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      self . _log_readers [ i ]  =  LogReader ( log_path ,  sort_by_time = self . sort_by_time ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self . _log_readers [ i ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  __iter__ ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  _inc ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    lr  =  self . _log_reader ( self . _current_log ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  self . _idx  <  len ( lr . _ents ) - 1 : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _idx  + =  1 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    else : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _idx  =  0 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      self . _current_log  =  next ( i  for  i  in  range ( self . _current_log  +  1 ,  len ( self . _log_readers )  +  1 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                               if  i  ==  len ( self . _log_readers )  or  self . _log_paths [ i ]  is  not  None ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  self . _current_log  ==  len ( self . _log_readers ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								        raise  StopIteration 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  __next__ ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    while  1 : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      lr  =  self . _log_reader ( self . _current_log ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      ret  =  lr . _ents [ self . _idx ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _inc ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  ret 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  tell ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # returns seconds from start of log 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  ( self . _log_reader ( self . _current_log ) . _ts [ self . _idx ]  -  self . start_time )  *  1e-9 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  seek ( self ,  ts ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # seek to nearest minute 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    minute  =  int ( ts / 60 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  minute  > =  len ( self . _log_paths )  or  self . _log_paths [ minute ]  is  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  False 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _current_log  =  minute 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # HACK: O(n) seek afterward 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _idx  =  0 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    while  self . tell ( )  <  ts : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _inc ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  True 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  reset ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . __init__ ( self . _log_paths ,  sort_by_time = self . sort_by_time ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								class  LogReader : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  __init__ ( self ,  fn ,  canonicalize = True ,  only_union_types = False ,  sort_by_time = False ,  dat = None ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    self . data_version  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _only_union_types  =  only_union_types 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    ext  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  not  dat : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _ ,  ext  =  os . path . splitext ( urllib . parse . urlparse ( fn ) . path ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ext  not  in  ( ' ' ,  ' .bz2 ' ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        # old rlogs weren't bz2 compressed 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        raise  Exception ( f " unknown extension  { ext } " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      with  FileReader ( fn )  as  f : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        dat  =  f . read ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  ext  ==  " .bz2 "  or  dat . startswith ( b ' BZh9 ' ) : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      dat  =  bz2 . decompress ( dat ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    ents  =  capnp_log . Event . read_multiple_bytes ( dat ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    _ents  =  [ ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      for  e  in  ents : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _ents . append ( e ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    except  capnp . KjException : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      warnings . warn ( " Corrupted events detected " ,  RuntimeWarning ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _ents  =  list ( sorted ( _ents ,  key = lambda  x :  x . logMonoTime )  if  sort_by_time  else  _ents ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    self . _ts  =  [ x . logMonoTime  for  x  in  self . _ents ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  @classmethod 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  from_bytes ( cls ,  dat ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  cls ( " " ,  dat = dat ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  __iter__ ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  ent  in  self . _ents : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      if  self . _only_union_types : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          ent . which ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          yield  ent 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        except  capnp . lib . capnp . KjException : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          pass 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      else : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        yield  ent 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  logreader_from_route_or_segment ( r ,  sort_by_time = False ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  sn  =  SegmentName ( r ,  allow_route_name = True ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  route  =  Route ( sn . route_name . canonical_name ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  if  sn . segment_num  <  0 : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  MultiLogIterator ( route . log_paths ( ) ,  sort_by_time = sort_by_time ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  else : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  LogReader ( route . log_paths ( ) [ sn . segment_num ] ,  sort_by_time = sort_by_time ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								if  __name__  ==  " __main__ " : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  import  codecs 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  # capnproto <= 0.8.0 throws errors converting byte data to string 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  # below line catches those errors and replaces the bytes with \x__ 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  codecs . register_error ( " strict " ,  codecs . backslashreplace_errors ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  log_path  =  sys . argv [ 1 ] 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  lr  =  LogReader ( log_path ,  sort_by_time = True ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  for  msg  in  lr : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    print ( msg )