| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -4,10 +4,8 @@ from functools import partial | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import multiprocessing | 
					 | 
					 | 
					 | 
					import multiprocessing | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import capnp | 
					 | 
					 | 
					 | 
					import capnp | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import enum | 
					 | 
					 | 
					 | 
					import enum | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import numpy as np | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import os | 
					 | 
					 | 
					 | 
					import os | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import pathlib | 
					 | 
					 | 
					 | 
					import pathlib | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import re | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import sys | 
					 | 
					 | 
					 | 
					import sys | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import tqdm | 
					 | 
					 | 
					 | 
					import tqdm | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import urllib.parse | 
					 | 
					 | 
					 | 
					import urllib.parse | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -21,7 +19,6 @@ from openpilot.common.swaglog import cloudlog | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url | 
					 | 
					 | 
					 | 
					from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.tools.lib.openpilotci import get_url | 
					 | 
					 | 
					 | 
					from openpilot.tools.lib.openpilotci import get_url | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.tools.lib.filereader import FileReader, file_exists, internal_source_available | 
					 | 
					 | 
					 | 
					from openpilot.tools.lib.filereader import FileReader, file_exists, internal_source_available | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.tools.lib.helpers import RE | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.tools.lib.route import Route, SegmentRange | 
					 | 
					 | 
					 | 
					from openpilot.tools.lib.route import Route, SegmentRange | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					LogMessage = Type[capnp._DynamicStructReader] | 
					 | 
					 | 
					 | 
					LogMessage = Type[capnp._DynamicStructReader] | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -79,19 +76,6 @@ class ReadMode(enum.StrEnum): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  AUTO_INTERACIVE = "i"  # default to rlogs, fallback to qlogs with a prompt from the user | 
					 | 
					 | 
					 | 
					  AUTO_INTERACIVE = "i"  # default to rlogs, fallback to qlogs with a prompt from the user | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def create_slice_from_string(s: str): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  m = re.fullmatch(RE.SLICE, s) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  assert m is not None, f"Invalid slice: {s}" | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  start, end, step = m.groups() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  start = int(start) if start is not None else None | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  end = int(end) if end is not None else None | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  step = int(step) if step is not None else None | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if start is not None and ":" not in s and end is None and step is None: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return start | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return slice(start, end, step) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def default_valid_file(fn): | 
					 | 
					 | 
					 | 
					def default_valid_file(fn): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return fn is not None and file_exists(fn) | 
					 | 
					 | 
					 | 
					  return fn is not None and file_exists(fn) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -121,27 +105,11 @@ def apply_strategy(mode: ReadMode, rlog_paths, qlog_paths, valid_file=default_va | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return auto_strategy(rlog_paths, qlog_paths, True, valid_file) | 
					 | 
					 | 
					 | 
					    return auto_strategy(rlog_paths, qlog_paths, True, valid_file) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def parse_slice(sr: SegmentRange): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  s = create_slice_from_string(sr._slice) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if isinstance(s, slice): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if s.stop is None or s.stop < 0 or (s.start is not None and s.start < 0):  # we need the number of segments in order to parse this slice | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      segs = np.arange(sr.get_max_seg_number() + 1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    else: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      segs = np.arange(s.stop + 1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return segs[s] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  else: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if s < 0: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      s = sr.get_max_seg_number() + s + 1 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return [s] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def comma_api_source(sr: SegmentRange, mode: ReadMode): | 
					 | 
					 | 
					 | 
					def comma_api_source(sr: SegmentRange, mode: ReadMode): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  segs = parse_slice(sr) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  route = Route(sr.route_name) | 
					 | 
					 | 
					 | 
					  route = Route(sr.route_name) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  rlog_paths = [route.log_paths()[seg] for seg in segs] | 
					 | 
					 | 
					 | 
					  rlog_paths = [route.log_paths()[seg] for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qlog_paths = [route.qlog_paths()[seg] for seg in segs] | 
					 | 
					 | 
					 | 
					  qlog_paths = [route.qlog_paths()[seg] for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  # comma api will have already checked if the file exists | 
					 | 
					 | 
					 | 
					  # comma api will have already checked if the file exists | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def valid_file(fn): | 
					 | 
					 | 
					 | 
					  def valid_file(fn): | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -154,30 +122,25 @@ def internal_source(sr: SegmentRange, mode: ReadMode): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if not internal_source_available(): | 
					 | 
					 | 
					 | 
					  if not internal_source_available(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    raise Exception("Internal source not available") | 
					 | 
					 | 
					 | 
					    raise Exception("Internal source not available") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  segs = parse_slice(sr) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def get_internal_url(sr: SegmentRange, seg, file): | 
					 | 
					 | 
					 | 
					  def get_internal_url(sr: SegmentRange, seg, file): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return f"cd:/{sr.dongle_id}/{sr.timestamp}/{seg}/{file}.bz2" | 
					 | 
					 | 
					 | 
					    return f"cd:/{sr.dongle_id}/{sr.timestamp}/{seg}/{file}.bz2" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  rlog_paths = [get_internal_url(sr, seg, "rlog") for seg in segs] | 
					 | 
					 | 
					 | 
					  rlog_paths = [get_internal_url(sr, seg, "rlog") for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qlog_paths = [get_internal_url(sr, seg, "qlog") for seg in segs] | 
					 | 
					 | 
					 | 
					  qlog_paths = [get_internal_url(sr, seg, "qlog") for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return apply_strategy(mode, rlog_paths, qlog_paths) | 
					 | 
					 | 
					 | 
					  return apply_strategy(mode, rlog_paths, qlog_paths) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def openpilotci_source(sr: SegmentRange, mode: ReadMode): | 
					 | 
					 | 
					 | 
					def openpilotci_source(sr: SegmentRange, mode: ReadMode): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  segs = parse_slice(sr) | 
					 | 
					 | 
					 | 
					  rlog_paths = [get_url(sr.route_name, seg, "rlog") for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					  qlog_paths = [get_url(sr.route_name, seg, "qlog") for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  rlog_paths = [get_url(sr.route_name, seg, "rlog") for seg in segs] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qlog_paths = [get_url(sr.route_name, seg, "qlog") for seg in segs] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return apply_strategy(mode, rlog_paths, qlog_paths) | 
					 | 
					 | 
					 | 
					  return apply_strategy(mode, rlog_paths, qlog_paths) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def comma_car_segments_source(sr: SegmentRange, mode=ReadMode.RLOG): | 
					 | 
					 | 
					 | 
					def comma_car_segments_source(sr: SegmentRange, mode=ReadMode.RLOG): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  segs = parse_slice(sr) | 
					 | 
					 | 
					 | 
					  return [get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs] | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return [get_comma_segments_url(sr.route_name, seg) for seg in segs] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def direct_source(file_or_url): | 
					 | 
					 | 
					 | 
					def direct_source(file_or_url): | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |