@ -2,18 +2,15 @@
# include <algorithm>
# include <array>
# include <atomic>
# include <bitset>
# include <cassert>
# include <cerrno>
# include <chrono>
# include <future>
# include <memory>
# include <thread>
# include <utility>
# include "cereal/gen/cpp/car.capnp.h"
# include "cereal/messaging/messaging.h"
# include "common/params.h"
# include "common/ratekeeper.h"
# include "common/swaglog.h"
# include "common/timing.h"
@ -43,9 +40,6 @@
# define MIN_IR_POWER 0.0f
# define CUTOFF_IL 400
# define SATURATE_IL 1000
using namespace std : : chrono_literals ;
std : : atomic < bool > ignition ( false ) ;
ExitHandler do_exit ;
@ -59,88 +53,6 @@ bool check_all_connected(const std::vector<Panda *> &pandas) {
return true ;
}
bool safety_setter_thread ( std : : vector < Panda * > pandas ) {
LOGD ( " Starting safety setter thread " ) ;
Params p ;
// there should be at least one panda connected
if ( pandas . size ( ) = = 0 ) {
return false ;
}
// initialize to ELM327 without OBD multiplexing for fingerprinting
bool obd_multiplexing_enabled = false ;
for ( int i = 0 ; i < pandas . size ( ) ; i + + ) {
pandas [ i ] - > set_safety_model ( cereal : : CarParams : : SafetyModel : : ELM327 , 1U ) ;
}
// openpilot can switch between multiplexing modes for different FW queries
while ( true ) {
if ( do_exit | | ! check_all_connected ( pandas ) | | ! ignition ) {
return false ;
}
bool obd_multiplexing_requested = p . getBool ( " ObdMultiplexingEnabled " ) ;
if ( obd_multiplexing_requested ! = obd_multiplexing_enabled ) {
for ( int i = 0 ; i < pandas . size ( ) ; i + + ) {
const uint16_t safety_param = ( i > 0 | | ! obd_multiplexing_requested ) ? 1U : 0U ;
pandas [ i ] - > set_safety_model ( cereal : : CarParams : : SafetyModel : : ELM327 , safety_param ) ;
}
obd_multiplexing_enabled = obd_multiplexing_requested ;
p . putBool ( " ObdMultiplexingChanged " , true ) ;
}
if ( p . getBool ( " FirmwareQueryDone " ) ) {
LOGW ( " finished FW query " ) ;
break ;
}
util : : sleep_for ( 20 ) ;
}
std : : string params ;
LOGW ( " waiting for params to set safety model " ) ;
while ( true ) {
if ( do_exit | | ! check_all_connected ( pandas ) | | ! ignition ) {
return false ;
}
if ( p . getBool ( " ControlsReady " ) ) {
params = p . get ( " CarParams " ) ;
if ( params . size ( ) > 0 ) break ;
}
util : : sleep_for ( 100 ) ;
}
LOGW ( " got %lu bytes CarParams " , params . size ( ) ) ;
AlignedBuffer aligned_buf ;
capnp : : FlatArrayMessageReader cmsg ( aligned_buf . align ( params . data ( ) , params . size ( ) ) ) ;
cereal : : CarParams : : Reader car_params = cmsg . getRoot < cereal : : CarParams > ( ) ;
cereal : : CarParams : : SafetyModel safety_model ;
uint16_t safety_param ;
auto safety_configs = car_params . getSafetyConfigs ( ) ;
uint16_t alternative_experience = car_params . getAlternativeExperience ( ) ;
for ( uint32_t i = 0 ; i < pandas . size ( ) ; i + + ) {
auto panda = pandas [ i ] ;
if ( safety_configs . size ( ) > i ) {
safety_model = safety_configs [ i ] . getSafetyModel ( ) ;
safety_param = safety_configs [ i ] . getSafetyParam ( ) ;
} else {
// If no safety mode is specified, default to silent
safety_model = cereal : : CarParams : : SafetyModel : : SILENT ;
safety_param = 0U ;
}
LOGW ( " panda %d: setting safety model: %d, param: %d, alternative experience: %d " , i , ( int ) safety_model , safety_param , alternative_experience ) ;
panda - > set_alternative_experience ( alternative_experience ) ;
panda - > set_safety_model ( safety_model , safety_param ) ;
}
return true ;
}
Panda * connect ( std : : string serial = " " , uint32_t index = 0 ) {
std : : unique_ptr < Panda > panda ;
try {
@ -197,16 +109,9 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) {
}
}
void can_recv_thread ( std : : vector < Panda * > pandas ) {
util : : set_thread_name ( " pandad_can_recv " ) ;
PubMaster pm ( { " can " } ) ;
// run at 100Hz
RateKeeper rk ( " pandad_can_recv " , 100 ) ;
std : : vector < can_frame > raw_can_data ;
while ( ! do_exit & & check_all_connected ( pandas ) ) {
void can_recv ( std : : vector < Panda * > & pandas , PubMaster * pm ) {
static std : : vector < can_frame > raw_can_data ;
{
bool comms_healthy = true ;
raw_can_data . clear ( ) ;
for ( const auto & panda : pandas ) {
@ -217,14 +122,12 @@ void can_recv_thread(std::vector<Panda *> pandas) {
auto evt = msg . initEvent ( ) ;
evt . setValid ( comms_healthy ) ;
auto canData = evt . initCan ( raw_can_data . size ( ) ) ;
for ( uin t i = 0 ; i < raw_can_data . size ( ) ; i + + ) {
for ( size_ t i = 0 ; i < raw_can_data . size ( ) ; + + i ) {
canData [ i ] . setAddress ( raw_can_data [ i ] . address ) ;
canData [ i ] . setDat ( kj : : arrayPtr ( ( uint8_t * ) raw_can_data [ i ] . dat . data ( ) , raw_can_data [ i ] . dat . size ( ) ) ) ;
canData [ i ] . setSrc ( raw_can_data [ i ] . src ) ;
}
pm . send ( " can " , msg ) ;
rk . keepTime ( ) ;
pm - > send ( " can " , msg ) ;
}
}
@ -386,7 +289,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
return ignition_local ;
}
void send_peripheral_state ( PubMaster * pm , Panda * panda ) {
void send_peripheral_state ( Panda * panda , PubMaster * pm ) {
// build msg
MessageBuilder msg ;
auto evt = msg . initEvent ( ) ;
@ -409,46 +312,23 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) {
pm - > send ( " peripheralState " , msg ) ;
}
void panda_state_thread ( std : : vector < Panda * > pandas , bool spoofing_started ) {
util : : set_thread_name ( " pandad_panda_state " ) ;
Params params ;
SubMaster sm ( { " controlsState " } ) ;
PubMaster pm ( { " pandaStates " , " peripheralState " } ) ;
Panda * peripheral_panda = pandas [ 0 ] ;
bool is_onroad = false ;
bool is_onroad_last = false ;
std : : future < bool > safety_future ;
void process_panda_state ( std : : vector < Panda * > & pandas , PubMaster * pm , bool spoofing_started ) {
static SubMaster sm ( { " controlsState " } ) ;
std : : vector < std : : string > connected_serials ;
for ( Panda * p : pandas ) {
connected_serials . push_back ( p - > hw_serial ( ) ) ;
}
LOGD ( " start panda state thread " ) ;
// run at 10hz
RateKeeper rk ( " panda_state_thread " , 10 ) ;
while ( ! do_exit & & check_all_connected ( pandas ) ) {
// send out peripheralState at 2Hz
if ( sm . frame % 5 = = 0 ) {
send_peripheral_state ( & pm , peripheral_panda ) ;
}
auto ignition_opt = send_panda_states ( & pm , pandas , spoofing_started ) ;
{
auto ignition_opt = send_panda_states ( pm , pandas , spoofing_started ) ;
if ( ! ignition_opt ) {
LOGE ( " Failed to get ignition_opt " ) ;
rk . keepTime ( ) ;
continue ;
return ;
}
ignition = * ignition_opt ;
// check if we should have pandad reconnect
if ( ! ignition ) {
if ( ! ignition_opt . value ( ) ) {
bool comms_healthy = true ;
for ( const auto & panda : pandas ) {
comms_healthy & = panda - > comms_healthy ( ) ;
@ -468,52 +348,28 @@ void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) {
}
}
}
if ( do_exit ) {
break ;
}
}
is_onroad = params . getBool ( " IsOnroad " ) ;
// set new safety on onroad transition, after params are cleared
if ( is_onroad & & ! is_onroad_last ) {
if ( ! safety_future . valid ( ) | | safety_future . wait_for ( 0 ms ) = = std : : future_status : : ready ) {
safety_future = std : : async ( std : : launch : : async , safety_setter_thread , pandas ) ;
} else {
LOGW ( " Safety setter thread already running " ) ;
}
}
is_onroad_last = is_onroad ;
sm . update ( 0 ) ;
const bool engaged = sm . allAliveAndValid ( { " controlsState " } ) & & sm [ " controlsState " ] . getControlsState ( ) . getEnabled ( ) ;
for ( const auto & panda : pandas ) {
panda - > send_heartbeat ( engaged ) ;
}
rk . keepTime ( ) ;
}
}
void process_peripheral_state ( Panda * panda , PubMaster * pm , bool no_fan_control ) {
static SubMaster sm ( { " deviceState " , " driverCameraState " } ) ;
void peripheral_control_thread ( Panda * panda , bool no_fan_control ) {
util : : set_thread_name ( " pandad_peripheral_control " ) ;
SubMaster sm ( { " deviceState " , " driverCameraState " } ) ;
uint64_t last_driver_camera_t = 0 ;
uint16_t prev_fan_speed = 999 ;
uint16_t ir_pwr = 0 ;
uint16_t prev_ir_pwr = 999 ;
static uint64_t last_driver_camera_t = 0 ;
static uint16_t prev_fan_speed = 999 ;
static uint16_t ir_pwr = 0 ;
static uint16_t prev_ir_pwr = 999 ;
FirstOrderFilter integ_lines_filter ( 0 , 30.0 , 0.05 ) ;
while ( ! do_exit & & panda - > connected ( ) ) {
sm . update ( 1000 ) ;
static FirstOrderFilter integ_lines_filter ( 0 , 30.0 , 0.05 ) ;
{
sm . update ( 0 ) ;
if ( sm . updated ( " deviceState " ) & & ! no_fan_control ) {
// Fan speed
uint16_t fan_speed = sm [ " deviceState " ] . getDeviceState ( ) . getFanSpeedPercentDesired ( ) ;
@ -551,9 +407,46 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
}
}
void pandad_main_thread ( std : : vector < std : : string > serials ) {
LOGW ( " launching pandad " ) ;
void pandad_run ( std : : vector < Panda * > & pandas ) {
const bool no_fan_control = getenv ( " NO_FAN_CONTROL " ) ! = nullptr ;
const bool spoofing_started = getenv ( " STARTED " ) ! = nullptr ;
const bool fake_send = getenv ( " FAKESEND " ) ! = nullptr ;
// Start the CAN send thread
std : : thread send_thread ( can_send_thread , pandas , fake_send ) ;
RateKeeper rk ( " pandad " , 100 ) ;
PubMaster pm ( { " can " , " pandaStates " , " peripheralState " } ) ;
PandaSafety panda_safety ( pandas ) ;
Panda * peripheral_panda = pandas [ 0 ] ;
// Main loop: receive CAN data and process states
while ( ! do_exit & & check_all_connected ( pandas ) ) {
can_recv ( pandas , & pm ) ;
// Process peripheral state at 20 Hz
if ( rk . frame ( ) % 5 = = 0 ) {
process_peripheral_state ( peripheral_panda , & pm , no_fan_control ) ;
}
// Process panda state at 10 Hz
if ( rk . frame ( ) % 10 = = 0 ) {
process_panda_state ( pandas , & pm , spoofing_started ) ;
panda_safety . configureSafetyMode ( ) ;
}
// Send out peripheralState at 2Hz
if ( rk . frame ( ) % 50 = = 0 ) {
send_peripheral_state ( peripheral_panda , & pm ) ;
}
rk . keepTime ( ) ;
}
send_thread . join ( ) ;
}
void pandad_main_thread ( std : : vector < std : : string > serials ) {
if ( serials . size ( ) = = 0 ) {
serials = Panda : : list ( ) ;
@ -585,16 +478,7 @@ void pandad_main_thread(std::vector<std::string> serials) {
if ( ! do_exit ) {
LOGW ( " connected to all pandas " ) ;
std : : vector < std : : thread > threads ;
threads . emplace_back ( panda_state_thread , pandas , getenv ( " STARTED " ) ! = nullptr ) ;
threads . emplace_back ( peripheral_control_thread , pandas [ 0 ] , getenv ( " NO_FAN_CONTROL " ) ! = nullptr ) ;
threads . emplace_back ( can_send_thread , pandas , getenv ( " FAKESEND " ) ! = nullptr ) ;
threads . emplace_back ( can_recv_thread , pandas ) ;
for ( auto & t : threads ) t . join ( ) ;
pandad_run ( pandas ) ;
}
for ( Panda * panda : pandas ) {