From 6579b300cec3353d7898289285f3f4460cfd52ef Mon Sep 17 00:00:00 2001 From: ccdunder Date: Fri, 21 Feb 2025 03:25:42 +0000 Subject: [PATCH 1/9] Bring libc.h over from panda to safety. This was removed when safety was moved from panda to opendbc. This is needed in order for memcpy to be available for safety tests as it is in production. --- opendbc/safety/board/libc.h | 77 +++++++++++++++++++++++++ opendbc/safety/tests/libsafety/safety.c | 1 + 2 files changed, 78 insertions(+) create mode 100644 opendbc/safety/board/libc.h diff --git a/opendbc/safety/board/libc.h b/opendbc/safety/board/libc.h new file mode 100644 index 0000000000..c5f0219350 --- /dev/null +++ b/opendbc/safety/board/libc.h @@ -0,0 +1,77 @@ +// **** libc **** + +void delay(uint32_t a) { + volatile uint32_t i; + for (i = 0; i < a; i++); +} + +void assert_fatal(bool condition, const char *msg) { + if (!condition) { + print("ASSERT FAILED\n"); + print(msg); + while (1) { + // hang + } + } +} + +// cppcheck-suppress misra-c2012-21.2 +void *memset(void *str, int c, unsigned int n) { + uint8_t *s = str; + for (unsigned int i = 0; i < n; i++) { + *s = c; + s++; + } + return str; +} + +#define UNALIGNED(X, Y) \ + (((uint32_t)(X) & (sizeof(uint32_t) - 1U)) | ((uint32_t)(Y) & (sizeof(uint32_t) - 1U))) + +// cppcheck-suppress misra-c2012-21.2 +void *memcpy(void *dest, const void *src, unsigned int len) { + unsigned int n = len; + uint8_t *d8 = dest; + const uint8_t *s8 = src; + + if ((n >= 4U) && !UNALIGNED(s8, d8)) { + uint32_t *d32 = (uint32_t *)d8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + const uint32_t *s32 = (const uint32_t *)s8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + + while(n >= 16U) { + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + n -= 16U; + } + + while(n >= 4U) { + *d32 = *s32; d32++; s32++; + n -= 4U; + } + + d8 = (uint8_t *)d32; + s8 = (const uint8_t *)s32; + } + while (n-- > 0U) { + *d8 = *s8; d8++; s8++; + } + return dest; +} + +// cppcheck-suppress misra-c2012-21.2 +int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { + int ret = 0; + const uint8_t *p1 = ptr1; + const uint8_t *p2 = ptr2; + for (unsigned int i = 0; i < num; i++) { + if (*p1 != *p2) { + ret = -1; + break; + } + p1++; + p2++; + } + return ret; +} diff --git a/opendbc/safety/tests/libsafety/safety.c b/opendbc/safety/tests/libsafety/safety.c index bdb63596b1..4035c4cc60 100644 --- a/opendbc/safety/tests/libsafety/safety.c +++ b/opendbc/safety/tests/libsafety/safety.c @@ -6,6 +6,7 @@ //int safety_tx_hook(CANPacket_t *to_send) { return 1; } #include "faults.h" +#include "libc.h" #include "safety.h" #include "drivers/can_common.h" From 0b267d8bccfe9cac53bf256a34a543e77e8aeaf9 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sat, 22 Feb 2025 19:31:03 +0000 Subject: [PATCH 2/9] safety refactor port --- opendbc/safety/safety/safety_hyundai_canfd.h | 190 ++++++++++++------- 1 file changed, 124 insertions(+), 66 deletions(-) diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index 9080d8af49..0e30488fcd 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -3,21 +3,28 @@ #include "safety_declarations.h" #include "safety_hyundai_common.h" -// *** Addresses checked in rx hook *** -// EV, ICE, HYBRID: ACCELERATOR (0x35), ACCELERATOR_BRAKE_ALT (0x100), ACCELERATOR_ALT (0x105) -#define HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \ - {.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ - {0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ - {0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}}}, \ - {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, \ - {0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }}}, \ - -// SCC_CONTROL (from ADAS unit or camera) -#define HYUNDAI_CANFD_SCC_ADDR_CHECK(scc_bus) \ - {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ +#define MAX_RX_CHECKS 16 + +static safety_config hyundai_canfd_init_safety_config(void) { + static RxCheck hyundai_canfd_rx_checks[MAX_RX_CHECKS] = {0}; + safety_config ret = { + .rx_checks = hyundai_canfd_rx_checks, + .rx_checks_len = 0, + .tx_msgs = NULL, + .tx_msgs_len = 0 + }; + return ret; +} + +static void add_rx_check(safety_config *safetyConfig, RxCheck config) { + const uint8_t index = safetyConfig->rx_checks_len; + if (index < (uint8_t)MAX_RX_CHECKS) { + safetyConfig->rx_checks_len++; + (void)memcpy(&safetyConfig->rx_checks[index], &config, sizeof(RxCheck)); + } else { + // TODO: Trigger safety_rx_checks_invalid. + } +} static bool hyundai_canfd_alt_buttons = false; static bool hyundai_canfd_lka_steering_alt = false; @@ -150,10 +157,16 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send) { } // cruise buttons check - if (addr == 0x1cf) { - int button = GET_BYTE(to_send, 2) & 0x7U; - bool is_cancel = (button == HYUNDAI_BTN_CANCEL); - bool is_resume = (button == HYUNDAI_BTN_RESUME); + const int button_addr = hyundai_canfd_alt_buttons ? 0x1aa : 0x1cf; + if (addr == button_addr) { + int cruise_button = 0; + if (addr == 0x1cf) { + cruise_button = GET_BYTE(to_send, 2) & 0x7U; + } else { + cruise_button = (GET_BYTE(to_send, 4) >> 4) & 0x7U; + } + bool is_cancel = (cruise_button == HYUNDAI_BTN_CANCEL); + bool is_resume = (cruise_button == HYUNDAI_BTN_RESUME); bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); if (!allowed) { @@ -206,12 +219,13 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { bool is_lfa_msg = ((addr == 0x12a) && !hyundai_canfd_lka_steering); // HUD icons - bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_lka_steering); + bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); + bool is_ccnc_msg = (((addr == 0x161) || (addr == 0x162)) && !hyundai_canfd_hda2); // SCC_CONTROL for camera SCC cars, we send our own longitudinal commands bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_lka_steering); - bool block_msg = is_lka_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; + bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_ccnc_msg || is_scc_msg; if (!block_msg) { bus_fwd = 0; } @@ -221,25 +235,35 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { } static safety_config hyundai_canfd_init(uint16_t param) { - const int HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT = 128; + const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128; const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; - static const CanMsg HYUNDAI_CANFD_LKA_STEERING_TX_MSGS[] = { + // TODO: Build these more precisely like RX_CHECKS. + static const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = { {0x50, 0, 16}, // LKAS {0x1CF, 1, 8}, // CRUISE_BUTTON + // TODO: Include this iff CANFD_ALT_BUTTONS + {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT {0x2A4, 0, 24}, // CAM_0x2A4 }; - static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[] = { {0x110, 0, 32}, // LKAS_ALT {0x1CF, 1, 8}, // CRUISE_BUTTON + // TODO: Include this iff CANFD_ALT_BUTTONS + {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT + // Needed for cruise control in case of ALT_BUTTONS. + {0x1A0, 1, 32}, // CRUISE_INFO {0x362, 0, 32}, // CAM_0x362 }; - static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[] = { {0x50, 0, 16}, // LKAS {0x1CF, 1, 8}, // CRUISE_BUTTON + // TODO: Include this iff CANFD_ALT_BUTTONS + {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT {0x2A4, 0, 24}, // CAM_0x2A4 + // LONG start {0x51, 0, 32}, // ADRV_0x51 {0x730, 1, 8}, // tester present for ADAS ECU disable {0x12A, 1, 16}, // LFA @@ -250,13 +274,41 @@ static safety_config hyundai_canfd_init(uint16_t param) { {0x200, 1, 8}, // ADRV_0x200 {0x345, 1, 8}, // ADRV_0x345 {0x1DA, 1, 32}, // ADRV_0x1da + {0x38C, 1, 32}, // ADRV_0x38c + {0x161, 1, 32}, // MSG_161 + {0x162, 1, 32}, // MSG_162 }; - static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_LONG_TX_MSGS[] = { + {0x110, 0, 32}, // LKAS_ALT + {0x1CF, 1, 8}, // CRUISE_BUTTON + // TODO: Include this iff CANFD_ALT_BUTTONS + {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT + {0x1A0, 1, 32}, // CRUISE_INFO + {0x362, 0, 32}, // CAM_0x362 + // LONG start + {0x51, 0, 32}, // ADRV_0x51 + {0x730, 1, 8}, // tester present for ADAS ECU disable + {0x12A, 1, 16}, // LFA + {0x160, 1, 16}, // ADRV_0x160 + {0x1E0, 1, 16}, // LFAHDA_CLUSTER + {0x1EA, 1, 32}, // ADRV_0x1ea + {0x200, 1, 8}, // ADRV_0x200 + {0x345, 1, 8}, // ADRV_0x345 + {0x1DA, 1, 32}, // ADRV_0x1da + {0x38C, 1, 32}, // ADRV_0x38c + {0x161, 1, 32}, // MSG_161 + {0x162, 1, 32}, // MSG_162 + }; + + static const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = { {0x12A, 0, 16}, // LFA {0x1A0, 0, 32}, // CRUISE_INFO {0x1CF, 2, 8}, // CRUISE_BUTTON + {0x1AA, 2, 16}, // CRUISE_BUTTONS_ALT {0x1E0, 0, 16}, // LFAHDA_CLUSTER + {0x161, 0, 32}, // MSG_161 + {0x162, 0, 32}, // MSG_162 }; @@ -266,57 +318,63 @@ static safety_config hyundai_canfd_init(uint16_t param) { hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS); hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT); - // no long for radar-SCC with LFA steering yet + // No long for radar-SCC with LFA steering yet. if (!hyundai_canfd_lka_steering && !hyundai_camera_scc) { hyundai_longitudinal = false; } - safety_config ret; + safety_config ret = hyundai_canfd_init_safety_config(); + + // RX Common checks. + const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; + add_rx_check(&ret, (RxCheck){.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); + add_rx_check(&ret, (RxCheck){.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); + add_rx_check(&ret, (RxCheck){.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); + + // Accel signals. + if (hyundai_ev_gas_signal) { + add_rx_check(&ret, (RxCheck){.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); + } else if (hyundai_hybrid_gas_signal) { + add_rx_check(&ret, (RxCheck){.msg = {{0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); + } else { + add_rx_check(&ret, (RxCheck){.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); + } + + // Cruise buttons. + if (hyundai_canfd_alt_buttons) { + add_rx_check(&ret, (RxCheck){.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); + } else { + add_rx_check(&ret, (RxCheck){.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}); + } + if (hyundai_longitudinal) { - if (hyundai_canfd_lka_steering) { - static RxCheck hyundai_canfd_lka_steering_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - }; + // SCC_CONTROL sent, not read. + } else { + // SCC_CONTROL read. + const int8_t scc_bus = hyundai_canfd_lka_steering ? 1 : hyundai_camera_scc ? 2 : 0; + add_rx_check(&ret, (RxCheck){.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); + } - ret = BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_long_rx_checks, HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS); + // TX checks. + if (hyundai_longitudinal) { + if (hyundai_canfd_lka_steering) { + if (hyundai_canfd_lka_steering_alt) { + // TODO: Support this. + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret); + } } else { - // Longitudinal checks for LFA steering - static RxCheck hyundai_canfd_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); } } else { if (hyundai_canfd_lka_steering) { - // *** LKA steering checks *** - // E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU. - // Does not use the alt buttons message - static RxCheck hyundai_canfd_lka_steering_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - HYUNDAI_CANFD_SCC_ADDR_CHECK(1) - }; - - ret = hyundai_canfd_lka_steering_alt ? BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_TX_MSGS); - } else if (!hyundai_camera_scc) { - // Radar sends SCC messages on these cars instead of camera - static RxCheck hyundai_canfd_radar_scc_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(0) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + if (hyundai_canfd_lka_steering_alt) { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret); + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret); + } } else { - // *** LFA steering checks *** - // Camera sends SCC messages on LFA steering cars. - // Both button messages exist on some platforms, so we ensure we track the correct one using flag - static RxCheck hyundai_canfd_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(2) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); } } From 3482e71d2fdfd66936a75e4560022f3d461b00c9 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sat, 22 Feb 2025 19:56:23 +0000 Subject: [PATCH 3/9] revert functional improvements --- opendbc/safety/safety/safety_hyundai_canfd.h | 69 ++++---------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index 0e30488fcd..e0bc9494e4 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -157,16 +157,11 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send) { } // cruise buttons check - const int button_addr = hyundai_canfd_alt_buttons ? 0x1aa : 0x1cf; - if (addr == button_addr) { - int cruise_button = 0; - if (addr == 0x1cf) { - cruise_button = GET_BYTE(to_send, 2) & 0x7U; - } else { - cruise_button = (GET_BYTE(to_send, 4) >> 4) & 0x7U; - } - bool is_cancel = (cruise_button == HYUNDAI_BTN_CANCEL); - bool is_resume = (cruise_button == HYUNDAI_BTN_RESUME); + // TODO: Support ALT_BUTTONS. + if (addr == 0x1cf) { + int button = GET_BYTE(to_send, 2) & 0x7U; + bool is_cancel = (button == HYUNDAI_BTN_CANCEL); + bool is_resume = (button == HYUNDAI_BTN_RESUME); bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); if (!allowed) { @@ -219,13 +214,12 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { bool is_lfa_msg = ((addr == 0x12a) && !hyundai_canfd_lka_steering); // HUD icons - bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); - bool is_ccnc_msg = (((addr == 0x161) || (addr == 0x162)) && !hyundai_canfd_hda2); + bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_lka_steering); // SCC_CONTROL for camera SCC cars, we send our own longitudinal commands bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_lka_steering); - bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_ccnc_msg || is_scc_msg; + bool block_msg = is_lka_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; if (!block_msg) { bus_fwd = 0; } @@ -235,35 +229,26 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { } static safety_config hyundai_canfd_init(uint16_t param) { - const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128; + const int HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT = 128; const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; - // TODO: Build these more precisely like RX_CHECKS. - static const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = { + // TODO: Build TX checks more precisely (like RX checks). + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_TX_MSGS[] = { {0x50, 0, 16}, // LKAS {0x1CF, 1, 8}, // CRUISE_BUTTON - // TODO: Include this iff CANFD_ALT_BUTTONS - {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT {0x2A4, 0, 24}, // CAM_0x2A4 }; - static const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS[] = { {0x110, 0, 32}, // LKAS_ALT {0x1CF, 1, 8}, // CRUISE_BUTTON - // TODO: Include this iff CANFD_ALT_BUTTONS - {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT - // Needed for cruise control in case of ALT_BUTTONS. - {0x1A0, 1, 32}, // CRUISE_INFO {0x362, 0, 32}, // CAM_0x362 }; - static const CanMsg HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = { {0x50, 0, 16}, // LKAS {0x1CF, 1, 8}, // CRUISE_BUTTON - // TODO: Include this iff CANFD_ALT_BUTTONS - {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT {0x2A4, 0, 24}, // CAM_0x2A4 - // LONG start {0x51, 0, 32}, // ADRV_0x51 {0x730, 1, 8}, // tester present for ADAS ECU disable {0x12A, 1, 16}, // LFA @@ -274,41 +259,13 @@ static safety_config hyundai_canfd_init(uint16_t param) { {0x200, 1, 8}, // ADRV_0x200 {0x345, 1, 8}, // ADRV_0x345 {0x1DA, 1, 32}, // ADRV_0x1da - {0x38C, 1, 32}, // ADRV_0x38c - {0x161, 1, 32}, // MSG_161 - {0x162, 1, 32}, // MSG_162 - }; - - static const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_LONG_TX_MSGS[] = { - {0x110, 0, 32}, // LKAS_ALT - {0x1CF, 1, 8}, // CRUISE_BUTTON - // TODO: Include this iff CANFD_ALT_BUTTONS - {0x1AA, 1, 16}, // CRUISE_BUTTONS_ALT - {0x1A0, 1, 32}, // CRUISE_INFO - {0x362, 0, 32}, // CAM_0x362 - // LONG start - {0x51, 0, 32}, // ADRV_0x51 - {0x730, 1, 8}, // tester present for ADAS ECU disable - {0x12A, 1, 16}, // LFA - {0x160, 1, 16}, // ADRV_0x160 - {0x1E0, 1, 16}, // LFAHDA_CLUSTER - {0x1EA, 1, 32}, // ADRV_0x1ea - {0x200, 1, 8}, // ADRV_0x200 - {0x345, 1, 8}, // ADRV_0x345 - {0x1DA, 1, 32}, // ADRV_0x1da - {0x38C, 1, 32}, // ADRV_0x38c - {0x161, 1, 32}, // MSG_161 - {0x162, 1, 32}, // MSG_162 }; - static const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = { + static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = { {0x12A, 0, 16}, // LFA {0x1A0, 0, 32}, // CRUISE_INFO {0x1CF, 2, 8}, // CRUISE_BUTTON - {0x1AA, 2, 16}, // CRUISE_BUTTONS_ALT {0x1E0, 0, 16}, // LFAHDA_CLUSTER - {0x161, 0, 32}, // MSG_161 - {0x162, 0, 32}, // MSG_162 }; From 1acfd9b0467ecfa10549689f6cd0def74aad74b3 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sat, 22 Feb 2025 21:13:47 +0000 Subject: [PATCH 4/9] Move to safety core and trigger rx_checks_invalid. --- opendbc/safety/safety.h | 28 +++++++++++++++++++- opendbc/safety/safety/safety_hyundai_canfd.h | 25 +---------------- opendbc/safety/safety_declarations.h | 4 +++ 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/opendbc/safety/safety.h b/opendbc/safety/safety.h index 913109957c..49081a52a1 100644 --- a/opendbc/safety/safety.h +++ b/opendbc/safety/safety.h @@ -80,6 +80,7 @@ bool vehicle_moving = false; bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 int cruise_button_prev = 0; bool safety_rx_checks_invalid = false; +bool add_check_failed = false; // for safety modes with torque steering control int desired_torque_last = 0; // last desired steer torque @@ -169,6 +170,30 @@ static void update_counter(RxCheck addr_list[], int index, uint8_t counter) { } } +// This is pretty arbirary. It's more than enough for now. +#define MAX_RX_CHECKS 16 + +static safety_config safety_config_init(void) { + static RxCheck rx_checks[MAX_RX_CHECKS] = {0}; + safety_config ret = { + .rx_checks = rx_checks, + .rx_checks_len = 0, + .tx_msgs = NULL, + .tx_msgs_len = 0 + }; + return ret; +} + +static void add_rx_check(safety_config *safetyConfig, RxCheck config) { + const uint8_t index = safetyConfig->rx_checks_len; + if (index < (uint8_t)MAX_RX_CHECKS) { + safetyConfig->rx_checks_len++; + (void)memcpy(&safetyConfig->rx_checks[index], &config, sizeof(RxCheck)); + } else { + add_check_failed = true; + } +} + static bool rx_msg_safety_check(const CANPacket_t *to_push, const safety_config *cfg, const safety_hooks *safety_hooks) { @@ -309,7 +334,7 @@ void safety_tick(const safety_config *cfg) { } } - safety_rx_checks_invalid = rx_checks_invalid; + safety_rx_checks_invalid = rx_checks_invalid || add_check_failed; } static void relay_malfunction_set(void) { @@ -418,6 +443,7 @@ int set_safety_hooks(uint16_t mode, uint16_t param) { controls_allowed = false; relay_malfunction_reset(); safety_rx_checks_invalid = false; + add_check_failed = false; current_safety_config.rx_checks = NULL; current_safety_config.rx_checks_len = 0; diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index e0bc9494e4..d0d5d7270b 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -3,29 +3,6 @@ #include "safety_declarations.h" #include "safety_hyundai_common.h" -#define MAX_RX_CHECKS 16 - -static safety_config hyundai_canfd_init_safety_config(void) { - static RxCheck hyundai_canfd_rx_checks[MAX_RX_CHECKS] = {0}; - safety_config ret = { - .rx_checks = hyundai_canfd_rx_checks, - .rx_checks_len = 0, - .tx_msgs = NULL, - .tx_msgs_len = 0 - }; - return ret; -} - -static void add_rx_check(safety_config *safetyConfig, RxCheck config) { - const uint8_t index = safetyConfig->rx_checks_len; - if (index < (uint8_t)MAX_RX_CHECKS) { - safetyConfig->rx_checks_len++; - (void)memcpy(&safetyConfig->rx_checks[index], &config, sizeof(RxCheck)); - } else { - // TODO: Trigger safety_rx_checks_invalid. - } -} - static bool hyundai_canfd_alt_buttons = false; static bool hyundai_canfd_lka_steering_alt = false; @@ -280,7 +257,7 @@ static safety_config hyundai_canfd_init(uint16_t param) { hyundai_longitudinal = false; } - safety_config ret = hyundai_canfd_init_safety_config(); + safety_config ret = safety_config_init(); // RX Common checks. const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; diff --git a/opendbc/safety/safety_declarations.h b/opendbc/safety/safety_declarations.h index b0e9588e95..780b057292 100644 --- a/opendbc/safety/safety_declarations.h +++ b/opendbc/safety/safety_declarations.h @@ -21,6 +21,7 @@ (config).tx_msgs_len = sizeof((tx)) / sizeof((tx)[0]); \ } while(0); + #define UPDATE_VEHICLE_SPEED(val_ms) (update_sample(&vehicle_speed, ROUND((val_ms) * VEHICLE_SPEED_FACTOR))) uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len); @@ -168,6 +169,8 @@ typedef struct { get_quality_flag_valid_t get_quality_flag_valid; } safety_hooks; +static safety_config safety_config_init(void); +static void add_rx_check(safety_config *safetyConfig, RxCheck config); bool safety_rx_hook(const CANPacket_t *to_push); bool safety_tx_hook(CANPacket_t *to_send); uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); @@ -206,6 +209,7 @@ extern bool vehicle_moving; extern bool acc_main_on; // referred to as "ACC off" in ISO 15622:2018 extern int cruise_button_prev; extern bool safety_rx_checks_invalid; +extern bool add_check_failed; // for safety modes with torque steering control extern int desired_torque_last; // last desired steer torque From 3f0f0ef5398ef2905781f968582cbe9472b66dbb Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sun, 23 Feb 2025 02:35:35 +0000 Subject: [PATCH 5/9] preprocess sans jinja --- opendbc/safety/safety/safety_hyundai_canfd.h | 109 ++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index d0d5d7270b..76f7089156 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -32,6 +32,9 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) { const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; const int scc_bus = hyundai_camera_scc ? 2 : pt_bus; + // TODO: this a bug? this what checks was using. + // const int8_t scc_bus = hyundai_canfd_lka_steering ? 1 : hyundai_camera_scc ? 2 : 0; + if (bus == pt_bus) { // driver torque if (addr == 0xea) { @@ -205,6 +208,106 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { return bus_fwd; } +/* + + +*/ + +/***************** +RX_CHECKS_CONFIG = [ + # Common. + { + DEFAULT: + """ + {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + """ + } + # Accel. + { + "hyundai_ev_gas_signal": + """ + {.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + """ + "hyundai_hybrid_gas_signal": + """ + {.msg = {{0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + """ + DEFAULT: + """ + {.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + """ + }, + + # Cruise. + { + "hyundai_canfd_alt_buttons": + """ + {.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, + """ + DEFAULT: + """ + {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, + """ + }, + + # SCC. + { + "hyundai_longitudinal": + # SCC_CONTROL sent, not read. + """" + {0}, + """ + DEFAULT: + # SCC_CONTROL read. + """ + {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, + """ + }, +] + +def gen_checks(): + config_strs = [] + for key_val_pairs in itertools.product(*[list(conf.items()) for conf in RX_CHECKS_CONFIG]): + checks = {} + for key, val in key_val_pairs: + checks[key] = val + config_str = "" + for check in checks: + if check is None: + continue + config_str += check + config_strs.append(config_str) + return "\n".join(config_strs) + + +def get_rx_checks(hyundai_ev_gas_signal, hyundai_hybrid_gas_signal, hyundai_canfd_alt_buttons, hyundai_longitudinal): + + +*****************/ + +static RxCheck* get_rx_checks() { + int pt_bus = 1; + int scc_bus = 2; + static RxCheck foo[] = { + {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + + + {.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + + + {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, + + + {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, + + }; + return foo; +} + static safety_config hyundai_canfd_init(uint16_t param) { const int HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT = 128; const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; @@ -245,7 +348,6 @@ static safety_config hyundai_canfd_init(uint16_t param) { {0x1E0, 0, 16}, // LFAHDA_CLUSTER }; - hyundai_common_init(param); gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut); @@ -258,7 +360,6 @@ static safety_config hyundai_canfd_init(uint16_t param) { } safety_config ret = safety_config_init(); - // RX Common checks. const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; add_rx_check(&ret, (RxCheck){.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); @@ -289,6 +390,10 @@ static safety_config hyundai_canfd_init(uint16_t param) { add_rx_check(&ret, (RxCheck){.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); } + //static const RxCheck RX_CHECKS[] = rx_checks(); + //RX_CHECKS + //rx_checks(); + // TX checks. if (hyundai_longitudinal) { if (hyundai_canfd_lka_steering) { From f96f0ba3726c00bcc247e64ef474e6e240059580 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sun, 23 Feb 2025 06:33:38 +0000 Subject: [PATCH 6/9] [draft] generate rx safety configs with jinja --- opendbc/safety/safety/generator.py | 106 +++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 opendbc/safety/safety/generator.py diff --git a/opendbc/safety/safety/generator.py b/opendbc/safety/safety/generator.py new file mode 100644 index 0000000000..adeda85f15 --- /dev/null +++ b/opendbc/safety/safety/generator.py @@ -0,0 +1,106 @@ +import jinja2 +import itertools +import pprint + +from jinja2 import Environment, StrictUndefined, Template + + +# Each identifier must already exist as a boolean in C. +# Each row is a list of mutually exclusive conditions. +# A default, naked else case is automatically added. +CONDITIONS = [ + ["hyundai_canfd_lka_steering", "hyundai_camera_scc"], + ["hyundai_ev_gas_signal", "hyundai_hybrid_gas_signal"], + ["hyundai_canfd_alt_buttons"], + ["hyundai_longitudinal"], +] + +# kwargs: dict[str, bool] +def gen_rx_check(**kwargs): + return env.from_string(""" +{%- set pt_bus = 1 if hyundai_canfd_lka_steering else 0 -%} +{%- set scc_bus = 1 if hyundai_canfd_lka_steering else (2 if hyundai_camera_scc else 0) -%} + +{#- RX Common checks. #} +{.msg = { {0x175, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{.msg = { {0xa0, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{.msg = { {0xea, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + +{#- Accel signals. -#} +{%- if hyundai_ev_gas_signal -%} + {.msg = { {0x35, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- elif hyundai_hybrid_gas_signal %} + {.msg = { {0x105, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- else %} + {.msg = { {0x100, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- endif -%} + +{#- Cruise signals. -#} +{%- if hyundai_canfd_alt_buttons %} + {.msg = { {0x1aa, ({{pt_bus}}), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{% else %} + {.msg = { {0x1cf, ({{pt_bus}}), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, +{% endif -%} + +{%- if hyundai_longitudinal %} + {#- SCC_CONTROL sent, not read. -#} +{% else %} + {#- // SCC_CONTROL read. -#} + {.msg = { {0x1a0, ({{scc_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{% endif -%} +""").render(**kwargs) + + +# Require every param to be defined. +env = Environment(undefined=StrictUndefined) + + +def generate_code(condition_dict): + # This function would be implemented externally + # It takes a dict of boolean names and their values + # and returns the appropriate code string + condition_str = ",\n".join([f"// {k}: {v}" for k, v in condition_dict.items()]) + return f"// Case:\n{condition_str}\n" + gen_rx_check(**condition_dict) + +template = Template('''\ +{%- macro generate_conditions(levels, current_conditions) -%} +{%- if levels -%} +{%- set current_level = levels[0] -%} +{%- set remaining_levels = levels[1:] -%} +{%- for condition in current_level -%} +{%- set new_conditions = current_conditions.copy() -%} +{%- set _ = new_conditions.update({condition: True}) -%} +{%- for other in current_level -%} + {%- if other != condition -%} + {%- set _ = new_conditions.update({other: False}) -%} + {%- endif -%} +{%- endfor %} +if ({{ condition }}) { +{{ generate_conditions(remaining_levels, new_conditions) | indent(2) }} +} +{%- endfor %} +else { +{%- set new_conditions = current_conditions.copy() -%} +{%- for condition in current_level -%} + {%- set _ = new_conditions.update({condition: False}) -%} +{%- endfor -%} +{{ generate_conditions(remaining_levels, new_conditions) | indent(2) }} +} +{%- else %} +{{ generate_code(current_conditions) }} +{%- endif -%} +{%- endmacro -%} + +void generated_function() { +{{ generate_conditions(condition_groups, {}) | indent(2) }} +} +''') + + +output = template.render(condition_groups=CONDITIONS, generate_code=generate_code) +print(output) +print(len(output.split("\n"))) + + + + From 854127db80149c6c8073be0d34f8a17223f3c2f4 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sun, 23 Feb 2025 06:58:10 +0000 Subject: [PATCH 7/9] revert the runtime dynamic rx check construction --- opendbc/safety/board/libc.h | 77 -------- opendbc/safety/safety.h | 28 +-- opendbc/safety/safety/safety_hyundai_canfd.h | 194 ++++--------------- opendbc/safety/safety_declarations.h | 4 - opendbc/safety/tests/libsafety/safety.c | 1 - 5 files changed, 42 insertions(+), 262 deletions(-) delete mode 100644 opendbc/safety/board/libc.h diff --git a/opendbc/safety/board/libc.h b/opendbc/safety/board/libc.h deleted file mode 100644 index c5f0219350..0000000000 --- a/opendbc/safety/board/libc.h +++ /dev/null @@ -1,77 +0,0 @@ -// **** libc **** - -void delay(uint32_t a) { - volatile uint32_t i; - for (i = 0; i < a; i++); -} - -void assert_fatal(bool condition, const char *msg) { - if (!condition) { - print("ASSERT FAILED\n"); - print(msg); - while (1) { - // hang - } - } -} - -// cppcheck-suppress misra-c2012-21.2 -void *memset(void *str, int c, unsigned int n) { - uint8_t *s = str; - for (unsigned int i = 0; i < n; i++) { - *s = c; - s++; - } - return str; -} - -#define UNALIGNED(X, Y) \ - (((uint32_t)(X) & (sizeof(uint32_t) - 1U)) | ((uint32_t)(Y) & (sizeof(uint32_t) - 1U))) - -// cppcheck-suppress misra-c2012-21.2 -void *memcpy(void *dest, const void *src, unsigned int len) { - unsigned int n = len; - uint8_t *d8 = dest; - const uint8_t *s8 = src; - - if ((n >= 4U) && !UNALIGNED(s8, d8)) { - uint32_t *d32 = (uint32_t *)d8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned - const uint32_t *s32 = (const uint32_t *)s8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned - - while(n >= 16U) { - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - n -= 16U; - } - - while(n >= 4U) { - *d32 = *s32; d32++; s32++; - n -= 4U; - } - - d8 = (uint8_t *)d32; - s8 = (const uint8_t *)s32; - } - while (n-- > 0U) { - *d8 = *s8; d8++; s8++; - } - return dest; -} - -// cppcheck-suppress misra-c2012-21.2 -int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { - int ret = 0; - const uint8_t *p1 = ptr1; - const uint8_t *p2 = ptr2; - for (unsigned int i = 0; i < num; i++) { - if (*p1 != *p2) { - ret = -1; - break; - } - p1++; - p2++; - } - return ret; -} diff --git a/opendbc/safety/safety.h b/opendbc/safety/safety.h index 49081a52a1..913109957c 100644 --- a/opendbc/safety/safety.h +++ b/opendbc/safety/safety.h @@ -80,7 +80,6 @@ bool vehicle_moving = false; bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 int cruise_button_prev = 0; bool safety_rx_checks_invalid = false; -bool add_check_failed = false; // for safety modes with torque steering control int desired_torque_last = 0; // last desired steer torque @@ -170,30 +169,6 @@ static void update_counter(RxCheck addr_list[], int index, uint8_t counter) { } } -// This is pretty arbirary. It's more than enough for now. -#define MAX_RX_CHECKS 16 - -static safety_config safety_config_init(void) { - static RxCheck rx_checks[MAX_RX_CHECKS] = {0}; - safety_config ret = { - .rx_checks = rx_checks, - .rx_checks_len = 0, - .tx_msgs = NULL, - .tx_msgs_len = 0 - }; - return ret; -} - -static void add_rx_check(safety_config *safetyConfig, RxCheck config) { - const uint8_t index = safetyConfig->rx_checks_len; - if (index < (uint8_t)MAX_RX_CHECKS) { - safetyConfig->rx_checks_len++; - (void)memcpy(&safetyConfig->rx_checks[index], &config, sizeof(RxCheck)); - } else { - add_check_failed = true; - } -} - static bool rx_msg_safety_check(const CANPacket_t *to_push, const safety_config *cfg, const safety_hooks *safety_hooks) { @@ -334,7 +309,7 @@ void safety_tick(const safety_config *cfg) { } } - safety_rx_checks_invalid = rx_checks_invalid || add_check_failed; + safety_rx_checks_invalid = rx_checks_invalid; } static void relay_malfunction_set(void) { @@ -443,7 +418,6 @@ int set_safety_hooks(uint16_t mode, uint16_t param) { controls_allowed = false; relay_malfunction_reset(); safety_rx_checks_invalid = false; - add_check_failed = false; current_safety_config.rx_checks = NULL; current_safety_config.rx_checks_len = 0; diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index 76f7089156..427a23797a 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -32,9 +32,6 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) { const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; const int scc_bus = hyundai_camera_scc ? 2 : pt_bus; - // TODO: this a bug? this what checks was using. - // const int8_t scc_bus = hyundai_canfd_lka_steering ? 1 : hyundai_camera_scc ? 2 : 0; - if (bus == pt_bus) { // driver torque if (addr == 0xea) { @@ -208,111 +205,10 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { return bus_fwd; } -/* - - -*/ - -/***************** -RX_CHECKS_CONFIG = [ - # Common. - { - DEFAULT: - """ - {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - """ - } - # Accel. - { - "hyundai_ev_gas_signal": - """ - {.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - """ - "hyundai_hybrid_gas_signal": - """ - {.msg = {{0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - """ - DEFAULT: - """ - {.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - """ - }, - - # Cruise. - { - "hyundai_canfd_alt_buttons": - """ - {.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, - """ - DEFAULT: - """ - {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, - """ - }, - - # SCC. - { - "hyundai_longitudinal": - # SCC_CONTROL sent, not read. - """" - {0}, - """ - DEFAULT: - # SCC_CONTROL read. - """ - {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, - """ - }, -] - -def gen_checks(): - config_strs = [] - for key_val_pairs in itertools.product(*[list(conf.items()) for conf in RX_CHECKS_CONFIG]): - checks = {} - for key, val in key_val_pairs: - checks[key] = val - config_str = "" - for check in checks: - if check is None: - continue - config_str += check - config_strs.append(config_str) - return "\n".join(config_strs) - - -def get_rx_checks(hyundai_ev_gas_signal, hyundai_hybrid_gas_signal, hyundai_canfd_alt_buttons, hyundai_longitudinal): - - -*****************/ - -static RxCheck* get_rx_checks() { - int pt_bus = 1; - int scc_bus = 2; - static RxCheck foo[] = { - {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - - - {.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, - - - {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, - - - {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, - - }; - return foo; -} - static safety_config hyundai_canfd_init(uint16_t param) { const int HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT = 128; const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; - // TODO: Build TX checks more precisely (like RX checks). static const CanMsg HYUNDAI_CANFD_LKA_STEERING_TX_MSGS[] = { {0x50, 0, 16}, // LKAS {0x1CF, 1, 8}, // CRUISE_BUTTON @@ -348,72 +244,64 @@ static safety_config hyundai_canfd_init(uint16_t param) { {0x1E0, 0, 16}, // LFAHDA_CLUSTER }; + hyundai_common_init(param); gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut); hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS); hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT); - // No long for radar-SCC with LFA steering yet. + // no long for radar-SCC with LFA steering yet if (!hyundai_canfd_lka_steering && !hyundai_camera_scc) { hyundai_longitudinal = false; } - safety_config ret = safety_config_init(); - // RX Common checks. - const int pt_bus = hyundai_canfd_lka_steering ? 1 : 0; - add_rx_check(&ret, (RxCheck){.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); - add_rx_check(&ret, (RxCheck){.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); - add_rx_check(&ret, (RxCheck){.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); - - // Accel signals. - if (hyundai_ev_gas_signal) { - add_rx_check(&ret, (RxCheck){.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); - } else if (hyundai_hybrid_gas_signal) { - add_rx_check(&ret, (RxCheck){.msg = {{0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); - } else { - add_rx_check(&ret, (RxCheck){.msg = {{0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}); - } - - // Cruise buttons. - if (hyundai_canfd_alt_buttons) { - add_rx_check(&ret, (RxCheck){.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); - } else { - add_rx_check(&ret, (RxCheck){.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}); - } - - if (hyundai_longitudinal) { - // SCC_CONTROL sent, not read. - } else { - // SCC_CONTROL read. - const int8_t scc_bus = hyundai_canfd_lka_steering ? 1 : hyundai_camera_scc ? 2 : 0; - add_rx_check(&ret, (RxCheck){.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}); - } - - //static const RxCheck RX_CHECKS[] = rx_checks(); - //RX_CHECKS - //rx_checks(); - - // TX checks. + safety_config ret; if (hyundai_longitudinal) { if (hyundai_canfd_lka_steering) { - if (hyundai_canfd_lka_steering_alt) { - // TODO: Support this. - } else { - SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret); - } + static RxCheck hyundai_canfd_lka_steering_long_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(1) + }; + + ret = BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_long_rx_checks, HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS); } else { - SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); + // Longitudinal checks for LFA steering + static RxCheck hyundai_canfd_long_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + }; + + ret = BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); } } else { if (hyundai_canfd_lka_steering) { - if (hyundai_canfd_lka_steering_alt) { - SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret); - } else { - SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret); - } + // *** LKA steering checks *** + // E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU. + // Does not use the alt buttons message + static RxCheck hyundai_canfd_lka_steering_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(1) + HYUNDAI_CANFD_SCC_ADDR_CHECK(1) + }; + + ret = hyundai_canfd_lka_steering_alt ? BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS) : \ + BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_TX_MSGS); + } else if (!hyundai_camera_scc) { + // Radar sends SCC messages on these cars instead of camera + static RxCheck hyundai_canfd_radar_scc_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(0) + }; + + ret = BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); } else { - SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); + // *** LFA steering checks *** + // Camera sends SCC messages on LFA steering cars. + // Both button messages exist on some platforms, so we ensure we track the correct one using flag + static RxCheck hyundai_canfd_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(2) + }; + + ret = BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); } } diff --git a/opendbc/safety/safety_declarations.h b/opendbc/safety/safety_declarations.h index 780b057292..b0e9588e95 100644 --- a/opendbc/safety/safety_declarations.h +++ b/opendbc/safety/safety_declarations.h @@ -21,7 +21,6 @@ (config).tx_msgs_len = sizeof((tx)) / sizeof((tx)[0]); \ } while(0); - #define UPDATE_VEHICLE_SPEED(val_ms) (update_sample(&vehicle_speed, ROUND((val_ms) * VEHICLE_SPEED_FACTOR))) uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len); @@ -169,8 +168,6 @@ typedef struct { get_quality_flag_valid_t get_quality_flag_valid; } safety_hooks; -static safety_config safety_config_init(void); -static void add_rx_check(safety_config *safetyConfig, RxCheck config); bool safety_rx_hook(const CANPacket_t *to_push); bool safety_tx_hook(CANPacket_t *to_send); uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); @@ -209,7 +206,6 @@ extern bool vehicle_moving; extern bool acc_main_on; // referred to as "ACC off" in ISO 15622:2018 extern int cruise_button_prev; extern bool safety_rx_checks_invalid; -extern bool add_check_failed; // for safety modes with torque steering control extern int desired_torque_last; // last desired steer torque diff --git a/opendbc/safety/tests/libsafety/safety.c b/opendbc/safety/tests/libsafety/safety.c index 4035c4cc60..bdb63596b1 100644 --- a/opendbc/safety/tests/libsafety/safety.c +++ b/opendbc/safety/tests/libsafety/safety.c @@ -6,7 +6,6 @@ //int safety_tx_hook(CANPacket_t *to_send) { return 1; } #include "faults.h" -#include "libc.h" #include "safety.h" #include "drivers/can_common.h" From 3d9ac9740744747317db6c7e6bc09670d81db205 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sun, 23 Feb 2025 09:25:30 +0000 Subject: [PATCH 8/9] [wip] productionize the jinja-based safety generator --- opendbc/safety/safety/generator.py | 104 ++++++++----------- opendbc/safety/safety/safety_hyundai_canfd.h | 96 ++++++++++------- opendbc/safety/tests/test_generator.py | 61 +++++++++++ 3 files changed, 161 insertions(+), 100 deletions(-) create mode 100644 opendbc/safety/tests/test_generator.py diff --git a/opendbc/safety/safety/generator.py b/opendbc/safety/safety/generator.py index adeda85f15..68794fdedc 100644 --- a/opendbc/safety/safety/generator.py +++ b/opendbc/safety/safety/generator.py @@ -1,68 +1,29 @@ +#!/usr/bin/env python3 +import os +import re +import glob +import subprocess import jinja2 import itertools import pprint -from jinja2 import Environment, StrictUndefined, Template +from functools import partial +from jinja2 import Environment, StrictUndefined -# Each identifier must already exist as a boolean in C. -# Each row is a list of mutually exclusive conditions. -# A default, naked else case is automatically added. -CONDITIONS = [ - ["hyundai_canfd_lka_steering", "hyundai_camera_scc"], - ["hyundai_ev_gas_signal", "hyundai_hybrid_gas_signal"], - ["hyundai_canfd_alt_buttons"], - ["hyundai_longitudinal"], -] +generator_path = os.path.dirname(os.path.realpath(__file__)) +include_pattern = re.compile(r'CM_ "IMPORT (.*?)";\n') +generated_suffix = '_generated.dbc' -# kwargs: dict[str, bool] -def gen_rx_check(**kwargs): - return env.from_string(""" -{%- set pt_bus = 1 if hyundai_canfd_lka_steering else 0 -%} -{%- set scc_bus = 1 if hyundai_canfd_lka_steering else (2 if hyundai_camera_scc else 0) -%} +# Require every template param to be defined when rendering. +def StrictTemplate(str): + return Environment(undefined=StrictUndefined).from_string(str) -{#- RX Common checks. #} -{.msg = { {0x175, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, -{.msg = { {0xa0, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{.msg = { {0xea, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{#- Accel signals. -#} -{%- if hyundai_ev_gas_signal -%} - {.msg = { {0x35, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- elif hyundai_hybrid_gas_signal %} - {.msg = { {0x105, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- else %} - {.msg = { {0x100, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- endif -%} - -{#- Cruise signals. -#} -{%- if hyundai_canfd_alt_buttons %} - {.msg = { {0x1aa, ({{pt_bus}}), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, -{% else %} - {.msg = { {0x1cf, ({{pt_bus}}), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, -{% endif -%} - -{%- if hyundai_longitudinal %} - {#- SCC_CONTROL sent, not read. -#} -{% else %} - {#- // SCC_CONTROL read. -#} - {.msg = { {0x1a0, ({{scc_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, -{% endif -%} -""").render(**kwargs) - - -# Require every param to be defined. -env = Environment(undefined=StrictUndefined) - - -def generate_code(condition_dict): - # This function would be implemented externally - # It takes a dict of boolean names and their values - # and returns the appropriate code string - condition_str = ",\n".join([f"// {k}: {v}" for k, v in condition_dict.items()]) - return f"// Case:\n{condition_str}\n" + gen_rx_check(**condition_dict) - -template = Template('''\ +# Args: +# condition_groups: list[list[condition: str]] +# generate_code: function() +EXPAND_CONDITIONS = StrictTemplate(''' {%- macro generate_conditions(levels, current_conditions) -%} {%- if levels -%} {%- set current_level = levels[0] -%} @@ -91,15 +52,34 @@ def generate_code(condition_dict): {%- endif -%} {%- endmacro -%} -void generated_function() { {{ generate_conditions(condition_groups, {}) | indent(2) }} -} ''') - -output = template.render(condition_groups=CONDITIONS, generate_code=generate_code) -print(output) -print(len(output.split("\n"))) +def generate_code(gencfg, condition_dict): + condition_str = ",\n".join([f"// {k}: {v}" for k, v in condition_dict.items()]) + return f"// Case:\n{condition_str}\n" + gencfg(**condition_dict) + +def remove_empty_lines(text): + return '\n'.join(line for line in text.splitlines() if line.strip()) + +# Args: +# template: str +# jinja template string which renders a safety config based on conditions. +# condition_groups: list[list[condition: str]] +# condition: an identifier of an existing boolean in C. +# These identifiers will be available in the template. +# list[condition]: mutually exclusive conditions. +# A default, naked else case is included automatically +# and does not need to be specified. +# e.g. [["boat_canfd", "boat_doip"], ["boat_long"]] +def generate_safety_config(template, condition_groups): + generate_code = StrictTemplate(template).render + + output = ("// Generated by opendbc/safety/generator/generator.py\n\n" + + EXPAND_CONDITIONS.render(condition_groups=condition_groups, + generate_code=generate_code) + ) + return remove_empty_lines(output) diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index 427a23797a..7e79dde3ed 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -257,57 +257,77 @@ static safety_config hyundai_canfd_init(uint16_t param) { } safety_config ret; + hyundai_canfd_init_rx_checks(&ret); + + // TX checks. if (hyundai_longitudinal) { if (hyundai_canfd_lka_steering) { - static RxCheck hyundai_canfd_lka_steering_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_long_rx_checks, HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS); + if (hyundai_canfd_lka_steering_alt) { + // TODO: Support this. + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret); + } } else { - // Longitudinal checks for LFA steering - static RxCheck hyundai_canfd_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); } } else { if (hyundai_canfd_lka_steering) { - // *** LKA steering checks *** - // E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU. - // Does not use the alt buttons message - static RxCheck hyundai_canfd_lka_steering_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - HYUNDAI_CANFD_SCC_ADDR_CHECK(1) - }; - - ret = hyundai_canfd_lka_steering_alt ? BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_rx_checks, HYUNDAI_CANFD_LKA_STEERING_TX_MSGS); - } else if (!hyundai_camera_scc) { - // Radar sends SCC messages on these cars instead of camera - static RxCheck hyundai_canfd_radar_scc_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(0) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + if (hyundai_canfd_lka_steering_alt) { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret); + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret); + } } else { - // *** LFA steering checks *** - // Camera sends SCC messages on LFA steering cars. - // Both button messages exist on some platforms, so we ensure we track the correct one using flag - static RxCheck hyundai_canfd_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(2) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_LFA_STEERING_TX_MSGS); + SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret); } } return ret; } +#define GENERATE_RX_CHECKS(func_name, params, template) \ + /* GENERATE_RX_CHECKS(#func_name, #params, #template) */ \ + +GENERATE_RX_CHECKS(hyundai_canfd_init_rx_checks, +R"[ + ['hyundai_canfd_lka_steering', 'hyundai_camera_scc'], + ['hyundai_ev_gas_signal', 'hyundai_hybrid_gas_signal'], + ['hyundai_canfd_alt_buttons'], + ['hyundai_longitudinal'], +]", +R" +{%- set pt_bus = 1 if hyundai_canfd_lka_steering else 0 -%} +{%- set scc_bus = 1 if hyundai_canfd_lka_steering else (2 if hyundai_camera_scc else 0) -%} + +{#- RX Common checks. #} +{.msg = { {0x175, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{.msg = { {0xa0, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{.msg = { {0xea, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, + +{#- Accel signals. -#} +{%- if hyundai_ev_gas_signal -%} + {.msg = { {0x35, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- elif hyundai_hybrid_gas_signal %} + {.msg = { {0x105, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- else %} + {.msg = { {0x100, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, +{%- endif -%} + +{#- Cruise signals. -#} +{%- if hyundai_canfd_alt_buttons %} + {.msg = { {0x1aa, ({{pt_bus}}), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{% else %} + {.msg = { {0x1cf, ({{pt_bus}}), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, +{% endif -%} + +{%- if hyundai_longitudinal %} + {#- SCC_CONTROL sent, not read. -#} +{% else %} + {#- // SCC_CONTROL read. -#} + {.msg = { {0x1a0, ({{scc_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, +{% endif -%} +"); + const safety_hooks hyundai_canfd_hooks = { .init = hyundai_canfd_init, .rx = hyundai_canfd_rx_hook, diff --git a/opendbc/safety/tests/test_generator.py b/opendbc/safety/tests/test_generator.py new file mode 100644 index 0000000000..3ee7e281fc --- /dev/null +++ b/opendbc/safety/tests/test_generator.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import unittest + +from opendbc.safety.safety.generator import generate_safety_config + + +class TestGenerator(unittest.TestCase): + def setUp(self): + self.maxDiff = None + pass + + def test_rx_hook(self): + CONDITIONS = [ + ["boat_canfd", "boat_doip"], + ["boat_long"], + ] + template = """ +{% if boat_canfd %} + canfd +{% endif %} +{% if boat_doip %} + doip +{% endif %} +{% if boat_long %} + long +{% endif %} +""" + output = generate_safety_config(template, CONDITIONS) + + self.assertMultiLineEqual(output, """ +// Generated by opendbc/safety/generator/generator.py + if (boat_canfd) { + if (boat_long) { + canfd + long + } + else { + canfd + } + } + if (boat_doip) { + if (boat_long) { + doip + long + } + else { + doip + } + } + else { + if (boat_long) { + long + } + else { + } + } +""".strip()) + + +if __name__ == "__main__": + unittest.main() From 2a570a01e23d802b3a1f982d6d59a3a778f73e19 Mon Sep 17 00:00:00 2001 From: ccdunder Date: Sun, 23 Feb 2025 13:04:49 +0000 Subject: [PATCH 9/9] gettin wild --- opendbc/safety/safety/generator.py | 57 ++++++-- opendbc/safety/safety/safety_hyundai_canfd.h | 41 +++--- opendbc/safety/safety_declarations.h | 5 + opendbc/safety/tests/test_generator.py | 144 +++++++++++++++---- 4 files changed, 189 insertions(+), 58 deletions(-) diff --git a/opendbc/safety/safety/generator.py b/opendbc/safety/safety/generator.py index 68794fdedc..429db62f4a 100644 --- a/opendbc/safety/safety/generator.py +++ b/opendbc/safety/safety/generator.py @@ -19,10 +19,23 @@ def StrictTemplate(str): return Environment(undefined=StrictUndefined).from_string(str) +def JinjaRenderer(template): + return StrictTemplate(template).render + +def PythonRenderer(template): + def render(**kwargs): + rx_checks = [] + ns = { + 'rx_checks': rx_checks, + 'add_rx_check': lambda check: rx_checks.append(check), + } + exec(template, ns, kwargs) + return '\n'.join(rx_checks) + return render # Args: # condition_groups: list[list[condition: str]] -# generate_code: function() +# render_outcome: function() EXPAND_CONDITIONS = StrictTemplate(''' {%- macro generate_conditions(levels, current_conditions) -%} {%- if levels -%} @@ -48,23 +61,32 @@ def StrictTemplate(str): {{ generate_conditions(remaining_levels, new_conditions) | indent(2) }} } {%- else %} -{{ generate_code(current_conditions) }} +{{ render_outcome(current_conditions) }} {%- endif -%} {%- endmacro -%} {{ generate_conditions(condition_groups, {}) | indent(2) }} ''') -def generate_code(gencfg, condition_dict): - condition_str = ",\n".join([f"// {k}: {v}" for k, v in condition_dict.items()]) - return f"// Case:\n{condition_str}\n" + gencfg(**condition_dict) +def expand_conditions(condition_groups, render_outcome): + with_comments = render_with_comments(render_outcome) + return EXPAND_CONDITIONS.render(condition_groups=condition_groups, + render_outcome=with_comments) + +def render_with_comments(render): + def wrapped(conditions): + condition_str = "\n".join([f"// {k}: {v}" for k, v in conditions.items()]) + return f"{condition_str}\n" + render(**conditions) + return wrapped -def remove_empty_lines(text): +def _remove_empty_lines(text): return '\n'.join(line for line in text.splitlines() if line.strip()) +# Generate C code to init a safety_config for any combination of conditions. +# # Args: # template: str -# jinja template string which renders a safety config based on conditions. +# jinja template for generating a safety config based on conditions. # condition_groups: list[list[condition: str]] # condition: an identifier of an existing boolean in C. # These identifiers will be available in the template. @@ -72,15 +94,22 @@ def remove_empty_lines(text): # A default, naked else case is included automatically # and does not need to be specified. # e.g. [["boat_canfd", "boat_doip"], ["boat_long"]] -def generate_safety_config(template, condition_groups): - generate_code = StrictTemplate(template).render +def generate_safety_config(template, condition_groups, + renderer=JinjaRenderer): + render = renderer(template) - output = ("// Generated by opendbc/safety/generator/generator.py\n\n" + - EXPAND_CONDITIONS.render(condition_groups=condition_groups, - generate_code=generate_code) - ) - return remove_empty_lines(output) + output = expand_conditions(condition_groups=condition_groups, + render_outcome=render) + return _remove_empty_lines(output) +def generate_rx_checks(fn_name, condition_groups, template): + generate_safety_config(template, condition_groups) + +def generate_init_header(filename): + output = ("// Generated by opendbc/safety/generator/generator.py\n\n" + + "" # TODO: fn content + ) + return output \ No newline at end of file diff --git a/opendbc/safety/safety/safety_hyundai_canfd.h b/opendbc/safety/safety/safety_hyundai_canfd.h index 7e79dde3ed..f6f9477e72 100644 --- a/opendbc/safety/safety/safety_hyundai_canfd.h +++ b/opendbc/safety/safety/safety_hyundai_canfd.h @@ -1,8 +1,12 @@ #pragma once #include "safety_declarations.h" +#include "safety_generated.h" #include "safety_hyundai_common.h" +GENERATE_SAFETY_CFG_INIT_HEADER("hyundai_canfd_init_generated.h", + hyundai_canfd_init_rx_checks) + static bool hyundai_canfd_alt_buttons = false; static bool hyundai_canfd_lka_steering_alt = false; @@ -285,19 +289,20 @@ static safety_config hyundai_canfd_init(uint16_t param) { return ret; } -#define GENERATE_RX_CHECKS(func_name, params, template) \ - /* GENERATE_RX_CHECKS(#func_name, #params, #template) */ \ +static void hyundai_canfd_init_rx_checks(safety_config *cfg) { +/* +generate_rx_checks( -GENERATE_RX_CHECKS(hyundai_canfd_init_rx_checks, -R"[ +condition_groups = [ ['hyundai_canfd_lka_steering', 'hyundai_camera_scc'], ['hyundai_ev_gas_signal', 'hyundai_hybrid_gas_signal'], ['hyundai_canfd_alt_buttons'], ['hyundai_longitudinal'], -]", -R" -{%- set pt_bus = 1 if hyundai_canfd_lka_steering else 0 -%} -{%- set scc_bus = 1 if hyundai_canfd_lka_steering else (2 if hyundai_camera_scc else 0) -%} +], + +template = """ +{% set pt_bus = 1 if hyundai_canfd_lka_steering else 0 %} +{% set scc_bus = 1 if hyundai_canfd_lka_steering else (2 if hyundai_camera_scc else 0) %} {#- RX Common checks. #} {.msg = { {0x175, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, @@ -305,28 +310,30 @@ R" {.msg = { {0xea, ({{pt_bus}}), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, {#- Accel signals. -#} -{%- if hyundai_ev_gas_signal -%} +{% if hyundai_ev_gas_signal %} {.msg = { {0x35, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- elif hyundai_hybrid_gas_signal %} +{% elif hyundai_hybrid_gas_signal %} {.msg = { {0x105, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- else %} +{% else %} {.msg = { {0x100, ({{pt_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, -{%- endif -%} +{% endif %} {#- Cruise signals. -#} -{%- if hyundai_canfd_alt_buttons %} +{% if hyundai_canfd_alt_buttons %} {.msg = { {0x1aa, ({{pt_bus}}), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, {% else %} {.msg = { {0x1cf, ({{pt_bus}}), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, -{% endif -%} +{% endif %} -{%- if hyundai_longitudinal %} +{% if hyundai_longitudinal %} {#- SCC_CONTROL sent, not read. -#} {% else %} {#- // SCC_CONTROL read. -#} {.msg = { {0x1a0, ({{scc_bus}}), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, -{% endif -%} -"); +{% endif %} +""") +*/ +} const safety_hooks hyundai_canfd_hooks = { .init = hyundai_canfd_init, diff --git a/opendbc/safety/safety_declarations.h b/opendbc/safety/safety_declarations.h index b0e9588e95..2e653eafe8 100644 --- a/opendbc/safety/safety_declarations.h +++ b/opendbc/safety/safety_declarations.h @@ -9,6 +9,11 @@ #define BUILD_SAFETY_CFG(rx, tx) ((safety_config){(rx), (sizeof((rx)) / sizeof((rx)[0])), \ (tx), (sizeof((tx)) / sizeof((tx)[0]))}) + +// filename will be generated containing the two fns +// based on the comment between the following macros. +#define GENERATE_SAFETY_CFG_INIT_HEADER(filename, fn_name) filename + #define SET_RX_CHECKS(rx, config) \ do { \ (config).rx_checks = (rx); \ diff --git a/opendbc/safety/tests/test_generator.py b/opendbc/safety/tests/test_generator.py index 3ee7e281fc..6448fbd59e 100644 --- a/opendbc/safety/tests/test_generator.py +++ b/opendbc/safety/tests/test_generator.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import unittest -from opendbc.safety.safety.generator import generate_safety_config +from opendbc.safety.safety.generator import generate_safety_config, PythonRenderer class TestGenerator(unittest.TestCase): @@ -9,52 +9,142 @@ def setUp(self): self.maxDiff = None pass - def test_rx_hook(self): + # Be a little more verbose to make it easy to update expected values. + def assertMultiLineEqual(self, expected, actual, msg=None): + msg = msg or f'actual:\n{actual}' + super().assertMultiLineEqual(expected.strip(), actual.strip(), msg) + + + def test_basic(self): CONDITIONS = [ - ["boat_canfd", "boat_doip"], - ["boat_long"], + ["g0e0", "g0e1"], + ["g1e0"], ] template = """ -{% if boat_canfd %} - canfd +{% if g0e0 %} + g0e0 +{% elif g0e1 %} + g0e1 +{% else %} + g0eN {% endif %} -{% if boat_doip %} - doip -{% endif %} -{% if boat_long %} - long +{% if g1e0 %} + g1e0 {% endif %} """ - output = generate_safety_config(template, CONDITIONS) + actual = generate_safety_config(template, CONDITIONS) - self.assertMultiLineEqual(output, """ -// Generated by opendbc/safety/generator/generator.py - if (boat_canfd) { - if (boat_long) { - canfd - long + expected = """ + if (g0e0) { + if (g1e0) { + // g0e0: True + // g0e1: False + // g1e0: True + g0e0 + g1e0 } else { - canfd + // g0e0: True + // g0e1: False + // g1e0: False + g0e0 } } - if (boat_doip) { - if (boat_long) { - doip - long + if (g0e1) { + if (g1e0) { + // g0e1: True + // g0e0: False + // g1e0: True + g0e1 + g1e0 } else { - doip + // g0e1: True + // g0e0: False + // g1e0: False + g0e1 } } else { - if (boat_long) { - long + if (g1e0) { + // g0e0: False + // g0e1: False + // g1e0: True + g0eN + g1e0 } else { + // g0e0: False + // g0e1: False + // g1e0: False + g0eN } } -""".strip()) +""" + self.assertMultiLineEqual(expected, actual) + + + def test_basic_python(self): + CONDITIONS = [ + ["g0e0", "g0e1"], + ["g1e0"], + ] + template = """ +if g0e0: + add_rx_check("g0e0") +elif g0e1: + add_rx_check("g0e1") +if g1e0: + add_rx_check("g1e0") +""" + actual = generate_safety_config(template, CONDITIONS, renderer=PythonRenderer) + + expected = """ + if (g0e0) { + if (g1e0) { + // g0e0: True + // g0e1: False + // g1e0: True + g0e0 + g1e0 + } + else { + // g0e0: True + // g0e1: False + // g1e0: False + g0e0 + } + } + if (g0e1) { + if (g1e0) { + // g0e1: True + // g0e0: False + // g1e0: True + g0e1 + g1e0 + } + else { + // g0e1: True + // g0e0: False + // g1e0: False + g0e1 + } + } + else { + if (g1e0) { + // g0e0: False + // g0e1: False + // g1e0: True + g1e0 + } + else { + // g0e0: False + // g0e1: False + // g1e0: False + } + } +""" + self.assertMultiLineEqual(expected, actual) if __name__ == "__main__":