#!/usr/bin/env python3
import argparse
import os
import time
import threading
import multiprocessing
from tqdm import tqdm
os . environ [ ' FILEREADER_CACHE ' ] = ' 1 '
from common . basedir import BASEDIR
from common . realtime import config_realtime_process , Ratekeeper , DT_CTRL
from selfdrive . boardd . boardd import can_capnp_to_can_list
from tools . plotjuggler . juggle import load_segment
from tools . lib . logreader import logreader_from_route_or_segment
from panda import Panda
try :
# this bool can be replaced when mypy understands this pattern
panda_jungle_imported = True
from panda_jungle import PandaJungle # pylint: disable=import-error # type: ignore
except ImportError :
PandaJungle = None
panda_jungle_imported = False
def send_thread ( s , flock ) :
if " Jungle " in str ( type ( s ) ) :
if " FLASH " in os . environ :
with flock :
s . flash ( )
for i in [ 0 , 1 , 2 , 3 , 0xFFFF ] :
s . can_clear ( i )
s . set_ignition ( False )
time . sleep ( 5 )
s . set_ignition ( True )
s . set_panda_power ( True )
else :
s . set_safety_mode ( Panda . SAFETY_ALLOUTPUT )
s . set_can_loopback ( False )
idx = 0
ign = True
rk = Ratekeeper ( 1 / DT_CTRL , print_delay_threshold = None )
while True :
# handle ignition cycling
if ENABLE_IGN :
i = ( rk . frame * DT_CTRL ) % ( IGN_ON + IGN_OFF ) < IGN_ON
if i != ign :
ign = i
s . set_ignition ( ign )
snd = CAN_MSGS [ idx ]
snd = list ( filter ( lambda x : x [ - 1 ] < = 2 , snd ) )
s . can_send_many ( snd )
idx = ( idx + 1 ) % len ( CAN_MSGS )
# Drain panda message buffer
s . can_recv ( )
rk . keep_time ( )
def connect ( ) :
config_realtime_process ( 3 , 55 )
serials = { }
flashing_lock = threading . Lock ( )
while True :
# look for new devices
for p in [ Panda , PandaJungle ] :
if p is None :
continue
for s in p . list ( ) :
if s not in serials :
print ( " starting send thread for " , s )
serials [ s ] = threading . Thread ( target = send_thread , args = ( p ( s ) , flashing_lock ) )
serials [ s ] . start ( )
# try to join all send threads
cur_serials = serials . copy ( )
for s , t in cur_serials . items ( ) :
t . join ( 0.01 )
if not t . is_alive ( ) :
del serials [ s ]
time . sleep ( 1 )
if __name__ == " __main__ " :
parser = argparse . ArgumentParser ( description = " Replay CAN messages from a route to all connected pandas and jungles in a loop. " ,
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
parser . add_argument ( " route_or_segment_name " , nargs = ' ? ' , help = " The route or segment name to replay. If not specified, a default public route will be used. " )
args = parser . parse_args ( )
if not panda_jungle_imported :
print ( " \33 [31m " , " WARNING: cannot connect to jungles. Clone the jungle library to enable support: " , " \033 [0m " )
print ( " \033 [34m " , f " cd { BASEDIR } && git clone https://github.com/commaai/panda_jungle " , " \033 [0m " )
print ( " Loading log... " )
if args . route_or_segment_name is None :
ROUTE = " 77611a1fac303767/2020-03-24--09-50-38 "
REPLAY_SEGS = list ( range ( 10 , 16 ) ) # route has 82 segments available
CAN_MSGS = [ ]
logs = [ f " https://commadataci.blob.core.windows.net/openpilotci/ { ROUTE } / { i } /rlog.bz2 " for i in REPLAY_SEGS ]
with multiprocessing . Pool ( 24 ) as pool :
for lr in tqdm ( pool . map ( load_segment , logs ) ) :
CAN_MSGS + = [ can_capnp_to_can_list ( m . can ) for m in lr if m . which ( ) == ' can ' ]
else :
lr = logreader_from_route_or_segment ( args . route_or_segment_name )
CAN_MSGS = [ can_capnp_to_can_list ( m . can ) for m in lr if m . which ( ) == ' can ' ]
# set both to cycle ignition
IGN_ON = int ( os . getenv ( " ON " , " 0 " ) )
IGN_OFF = int ( os . getenv ( " OFF " , " 0 " ) )
ENABLE_IGN = IGN_ON > 0 and IGN_OFF > 0
if ENABLE_IGN :
print ( f " Cycling ignition: on for { IGN_ON } s, off for { IGN_OFF } s " )
connect ( )