From 6939b2d4d85d8a24fa99c75062896a1278ca8c79 Mon Sep 17 00:00:00 2001 From: Edwin <20777515+Lucien950@users.noreply.github.com> Date: Sat, 28 Dec 2024 16:15:31 -0800 Subject: [PATCH] Heartbeat Refactor (#1325) ### Changelist - Changed structure of heartbeat monitor config. (see `firmware/shared/src/app/app_heartbeatMonitorBoard.h`) - Gives sturcture towards future app level refactor ### Testing Done - fixed tests for all boards - adapted shared heartbeat monitor to new structure ### Resolved Tickets --- firmware/cmake/shared.cmake | 12 +- firmware/cmake/tests.cmake | 7 +- firmware/logfs/CMakeLists.txt | 3 + firmware/quadruna/BMS/CMakeLists.txt | 1 + firmware/quadruna/BMS/src/app/app_globals.h | 1 - .../BMS/src/app/app_heartbeatMonitors.c | 20 + .../BMS/src/app/app_heartbeatMonitors.h | 5 + .../BMS/src/app/states/app_allStates.c | 7 +- .../BMS/src/app/states/app_initState.c | 4 +- firmware/quadruna/BMS/src/tasks.c | 39 -- .../BMS/test/test_bmsBaseStateMachineTest.h | 48 +-- firmware/quadruna/BMS/test/test_faults.cpp | 3 +- firmware/quadruna/BMS/test/test_imd.h | 12 +- .../quadruna/BMS/test/test_stateMachine.cpp | 2 +- firmware/quadruna/CRIT/CMakeLists.txt | 1 + .../CRIT/src/app/app_heartbeatMonitors.c | 37 ++ .../CRIT/src/app/app_heartbeatMonitors.h | 5 + .../quadruna/CRIT/src/app/app_mainState.c | 6 +- firmware/quadruna/CRIT/src/tasks.c | 50 --- .../CRIT/test/test_critBaseStateMachineTest.h | 58 +-- firmware/quadruna/RSM/CMakeLists.txt | 1 + .../RSM/src/app/app_heartbeatMonitors.c | 31 ++ .../RSM/src/app/app_heartbeatMonitors.h | 5 + firmware/quadruna/RSM/src/app/app_mainState.c | 11 +- firmware/quadruna/RSM/src/tasks.c | 49 +-- firmware/quadruna/RSM/test/test_faults.cpp | 84 ---- .../RSM/test/test_rsmBaseStateMachineTest.h | 52 +-- firmware/quadruna/VC/CMakeLists.txt | 1 + .../VC/src/app/app_heartbeatMonitors.c | 38 ++ .../VC/src/app/app_heartbeatMonitors.h | 5 + .../VC/src/app/states/app_allStates.c | 6 +- firmware/quadruna/VC/src/tasks.c | 51 --- .../quadruna/VC/test/test_stateMachine.cpp | 10 +- .../VC/test/test_vcBaseStateMachineTest.h | 63 +-- firmware/shared/CMakeLists.txt | 31 +- firmware/shared/src/app/app_heartbeatBoard.c | 49 +++ firmware/shared/src/app/app_heartbeatBoard.h | 59 +++ .../shared/src/app/app_heartbeatMonitor.c | 135 +------ .../shared/src/app/app_heartbeatMonitor.h | 86 +---- .../shared/test/app_heartbeatBoardsEnum.h | 12 - .../shared/test/test_heartbeatMonitor.cpp | 361 +++--------------- 41 files changed, 418 insertions(+), 1043 deletions(-) create mode 100644 firmware/quadruna/BMS/src/app/app_heartbeatMonitors.c create mode 100644 firmware/quadruna/BMS/src/app/app_heartbeatMonitors.h create mode 100644 firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.c create mode 100644 firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.h create mode 100644 firmware/quadruna/RSM/src/app/app_heartbeatMonitors.c create mode 100644 firmware/quadruna/RSM/src/app/app_heartbeatMonitors.h create mode 100644 firmware/quadruna/VC/src/app/app_heartbeatMonitors.c create mode 100644 firmware/quadruna/VC/src/app/app_heartbeatMonitors.h create mode 100644 firmware/shared/src/app/app_heartbeatBoard.c create mode 100644 firmware/shared/src/app/app_heartbeatBoard.h delete mode 100644 firmware/shared/test/app_heartbeatBoardsEnum.h diff --git a/firmware/cmake/shared.cmake b/firmware/cmake/shared.cmake index 2d0daf0d1f..e7c7729909 100644 --- a/firmware/cmake/shared.cmake +++ b/firmware/cmake/shared.cmake @@ -19,6 +19,10 @@ set(SHARED_EMBEDDED_DIR_CPP "${SHARED_DIR}/srcpp") set(SHARED_APP_INCLUDE_DIR_CPP "${SHARED_EMBEDDED_DIR_CPP}/app") set(SHARED_IO_INCLUDE_DIR_CPP "${SHARED_EMBEDDED_DIR_CPP}/io") set(SHARED_HW_INCLUDE_DIR_CPP "${SHARED_EMBEDDED_DIR_CPP}/hw") +# code sources +file(GLOB_RECURSE SHARED_APP_SRCS "${SHARED_APP_INCLUDE_DIR}/*.c") +file(GLOB_RECURSE SHARED_IO_SRCS "${SHARED_IO_INCLUDE_DIR}/*.c") +file(GLOB_RECURSE SHARED_HW_SRCS "${SHARED_HW_INCLUDE_DIR}/*.c") # Test Utils set(SHARED_TEST_UTILS_INCLUDE_DIRS "${SHARED_DIR}/test_utils") @@ -69,13 +73,13 @@ endfunction() function(jsoncan_library BOARD CAR JSONCAN_DIR) jsoncan_sources( ${BOARD} - "${JSONCAN_DIR}" + ${JSONCAN_DIR} FALSE ${CAR} ) add_library( - "${CAR}_${BOARD}_jsoncan" STATIC - "${CAN_SRCS}" + "${CAR}_${BOARD}_jsoncan" INTERFACE ) - target_include_directories("${CAR}_${BOARD}_jsoncan" PUBLIC "${CAN_INCLUDE_DIRS}") + target_sources("${CAR}_${BOARD}_jsoncan" INTERFACE ${CAN_SRCS}) + target_include_directories("${CAR}_${BOARD}_jsoncan" INTERFACE "${CAN_INCLUDE_DIRS}") endfunction() \ No newline at end of file diff --git a/firmware/cmake/tests.cmake b/firmware/cmake/tests.cmake index c9de799198..fee8d46ac7 100644 --- a/firmware/cmake/tests.cmake +++ b/firmware/cmake/tests.cmake @@ -23,7 +23,6 @@ function(compile_gtest_executable target_compile_options(${TEST_EXECUTABLE_NAME} PUBLIC -Wall - -g3 ) target_link_libraries(${TEST_EXECUTABLE_NAME} gtest_main) add_test(NAME ${TEST_EXECUTABLE_NAME} @@ -61,9 +60,9 @@ function(create_fake_library add_library(${LIB_NAME} STATIC ${FAKE_SRCS}) target_compile_options(${LIB_NAME} - PUBLIC - -Wall - -g3 + PUBLIC + -Wall + -g3 ) target_include_directories(${LIB_NAME} PUBLIC diff --git a/firmware/logfs/CMakeLists.txt b/firmware/logfs/CMakeLists.txt index d01b25faf4..be7d839095 100644 --- a/firmware/logfs/CMakeLists.txt +++ b/firmware/logfs/CMakeLists.txt @@ -1,3 +1,6 @@ +message("") +message("⚙️ Configuring LogFS") + set(LOGFS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src" ) diff --git a/firmware/quadruna/BMS/CMakeLists.txt b/firmware/quadruna/BMS/CMakeLists.txt index 5544c20736..8c3b4578b0 100644 --- a/firmware/quadruna/BMS/CMakeLists.txt +++ b/firmware/quadruna/BMS/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND APP_SRCS "${SHARED_APP_INCLUDE_DIR}/app_timer.c" "${SHARED_APP_INCLUDE_DIR}/app_math.c" "${SHARED_APP_INCLUDE_DIR}/app_heartbeatMonitor.c" + "${SHARED_APP_INCLUDE_DIR}/app_heartbeatBoard.c" "${SHARED_APP_INCLUDE_DIR}/app_shdnLoopNode.c" ) set(APP_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src/app" "${SHARED_APP_INCLUDE_DIR}" "${SHARED_APP_INCLUDE_QUADRUNA_DIR}") diff --git a/firmware/quadruna/BMS/src/app/app_globals.h b/firmware/quadruna/BMS/src/app/app_globals.h index 073e5d06c3..7071ea70de 100644 --- a/firmware/quadruna/BMS/src/app/app_globals.h +++ b/firmware/quadruna/BMS/src/app/app_globals.h @@ -3,7 +3,6 @@ #include #include "io_faultLatch.h" #include "io_thermistors.h" -#include "app_heartbeatMonitor.h" #include "app_timer.h" typedef struct diff --git a/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.c b/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.c new file mode 100644 index 0000000000..3db28f1b56 --- /dev/null +++ b/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.c @@ -0,0 +1,20 @@ +#include "app_heartbeatMonitors.h" +#include "app_canRx.h" +#include "app_canAlerts.h" +#include "app_canTx.h" + +static HeartbeatBoard heartbeat_boards[1] = { + // vc + { + .getter = app_canRx_VC_Heartbeat_get, + .resetter = app_canRx_VC_Heartbeat_update, + .fault_setter = app_canAlerts_BMS_Warning_MissingVCHeartbeat_set, + .fault_getter = app_canAlerts_BMS_Warning_MissingVCHeartbeat_get, + .timeout_ms = 200, + } +}; + +HeartbeatMonitor hb_monitor = { .boards = heartbeat_boards, + .board_count = 1, + .block_faults = false, + .own_heartbeat = app_canTx_BMS_Heartbeat_set }; diff --git a/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.h b/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.h new file mode 100644 index 0000000000..d505893a9b --- /dev/null +++ b/firmware/quadruna/BMS/src/app/app_heartbeatMonitors.h @@ -0,0 +1,5 @@ +#pragma once + +#include "app_heartbeatMonitor.h" + +extern HeartbeatMonitor hb_monitor; \ No newline at end of file diff --git a/firmware/quadruna/BMS/src/app/states/app_allStates.c b/firmware/quadruna/BMS/src/app/states/app_allStates.c index d4eaf74950..ea0ca40391 100644 --- a/firmware/quadruna/BMS/src/app/states/app_allStates.c +++ b/firmware/quadruna/BMS/src/app/states/app_allStates.c @@ -1,7 +1,5 @@ #include "states/app_allStates.h" #include "states/app_faultState.h" -#include "app_utils.h" -#include "app_thermistors.h" #include "app_accumulator.h" #include "app_tractiveSystem.h" #include "app_imd.h" @@ -11,6 +9,7 @@ #include "io_faultLatch.h" #include "io_airs.h" #include "io_bspdTest.h" +#include "app_heartbeatMonitors.h" // Num of cycles for voltage and cell temperature values to settle #define NUM_CYCLES_TO_SETTLE (30U) @@ -55,8 +54,8 @@ bool app_allStates_runOnTick100Hz(void) { app_canTx_BMS_Heartbeat_set(true); - app_heartbeatMonitor_checkIn(); - app_heartbeatMonitor_broadcastFaults(); + app_heartbeatMonitor_checkIn(&hb_monitor); + app_heartbeatMonitor_broadcastFaults(&hb_monitor); const bool balancing_enabled = app_canRx_Debug_CellBalancingRequest_get(); diff --git a/firmware/quadruna/BMS/src/app/states/app_initState.c b/firmware/quadruna/BMS/src/app/states/app_initState.c index 89d874b43f..53517969a6 100644 --- a/firmware/quadruna/BMS/src/app/states/app_initState.c +++ b/firmware/quadruna/BMS/src/app/states/app_initState.c @@ -1,10 +1,10 @@ #include "states/app_allStates.h" -#include "app_utils.h" #include "app_imd.h" #include "app_soc.h" #include "io_faultLatch.h" #include "io_airs.h" #include "app_inverterOnState.h" +#include "app_heartbeatMonitors.h" #define TS_DISCHARGED_THRESHOLD_V (10.0f) @@ -45,7 +45,7 @@ static void initStateRunOnTick100Hz(void) { const bool air_negative_closed = io_airs_isNegativeClosed(); const bool ts_discharged = app_tractiveSystem_getVoltage() < TS_DISCHARGED_THRESHOLD_V; - const bool missing_hb = app_heartbeatMonitor_isSendingMissingHeartbeatFault(); + const bool missing_hb = app_heartbeatMonitor_isSendingMissingHeartbeatFault(&hb_monitor); if (air_negative_closed && ts_discharged) { diff --git a/firmware/quadruna/BMS/src/tasks.c b/firmware/quadruna/BMS/src/tasks.c index 582fc39282..a5cc4e2e28 100644 --- a/firmware/quadruna/BMS/src/tasks.c +++ b/firmware/quadruna/BMS/src/tasks.c @@ -211,41 +211,6 @@ static const GlobalsConfig globals_config = { .bms_ok_latch = &bms_ok_latch, .imd_ok_latch = &imd_ok_latch, .bspd_ok_latch = &bspd_ok_latch }; -// config for heartbeat monitor (can funcs and flags) -// BMS relies on VC -static const bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = false, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = false, - [FSM_HEARTBEAT_BOARD] = false, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false -}; - -// heartbeatGetters - get heartbeat signals from other boards -static bool (*const heartbeatGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatUpdaters - update local CAN table with heartbeat status -static void (*const heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatFaultSetters - broadcast heartbeat faults over CAN -static void (*const heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canAlerts_BMS_Warning_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatFaultGetters - gets fault statuses over CAN -static bool (*const heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canAlerts_BMS_Warning_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL -}; - const Gpio *id_to_gpio[] = { [BMS_GpioNetName_ACCEL_BRAKE_OK_3V3] = &accel_brake_ok_pin, [BMS_GpioNetName_AIR_P_EN] = &airs_config.air_p_gpio, [BMS_GpioNetName_AUX_TSENSE_MUX0] = &thermistors_config.mux_0_gpio, @@ -335,10 +300,6 @@ void tasks_init(void) app_globals_init(&globals_config); app_stateMachine_init(app_initState_get()); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_BMS_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); - // broadcast commit info app_canTx_BMS_Hash_set(GIT_COMMIT_HASH); app_canTx_BMS_Clean_set(GIT_COMMIT_CLEAN); diff --git a/firmware/quadruna/BMS/test/test_bmsBaseStateMachineTest.h b/firmware/quadruna/BMS/test/test_bmsBaseStateMachineTest.h index 2dc24379e3..171a4bf986 100644 --- a/firmware/quadruna/BMS/test/test_bmsBaseStateMachineTest.h +++ b/firmware/quadruna/BMS/test/test_bmsBaseStateMachineTest.h @@ -3,7 +3,6 @@ #include #include "test_baseStateMachineTest.h" -#include "fake_io_time.hpp" #include "fake_io_led.hpp" #include "fake_io_airs.hpp" #include "fake_io_sd.hpp" @@ -21,7 +20,7 @@ extern "C" #include "app_canRx.h" #include "app_canAlerts.h" #include "app_canUtils.h" -#include "app_heartbeatMonitor.h" +#include "app_heartbeatMonitors.h" #include "app_stateMachine.h" #include "app_utils.h" #include "states/app_initState.h" @@ -48,9 +47,9 @@ class BmsBaseStateMachineTest : public BaseStateMachineTest app_canTx_init(); app_canRx_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_BMS_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); + // Disable heartbeat monitor in the nominal case. To use representative heartbeat behavior, + // re-enable the heartbeat monitor. + app_heartbeatMonitor_blockFaults(&hb_monitor, true); app_accumulator_init(); app_tractiveSystem_init(); @@ -76,10 +75,6 @@ class BmsBaseStateMachineTest : public BaseStateMachineTest // Default to starting the state machine in the `init` state app_stateMachine_init(app_initState_get()); - - // Disable heartbeat monitor in the nominal case. To use representative heartbeat behavior, - // re-enable the heartbeat monitor. - app_heartbeatMonitor_blockFaults(true); } void TearDown() override @@ -121,41 +116,6 @@ class BmsBaseStateMachineTest : public BaseStateMachineTest const FaultLatch imd_ok_latch = {}; const FaultLatch bspd_ok_latch = {}; - // config for heartbeat monitor (can funcs and flags) - // BMS relies on VC - bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = false, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = false, - [FSM_HEARTBEAT_BOARD] = false, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false - }; - - // heartbeatGetters - get heartbeat signals from other boards - bool (*heartbeatGetters[HEARTBEAT_BOARD_COUNT])() = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatUpdaters - update local CAN table with heartbeat status - void (*heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatFaultSetters - broadcast heartbeat faults over CAN - void (*heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canAlerts_BMS_Warning_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatFaultGetters - gets fault statuses over CAN - bool (*heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])() = { - [BMS_HEARTBEAT_BOARD] = NULL, [VC_HEARTBEAT_BOARD] = app_canAlerts_BMS_Warning_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, [FSM_HEARTBEAT_BOARD] = NULL, - [DIM_HEARTBEAT_BOARD] = NULL, [CRIT_HEARTBEAT_BOARD] = NULL - }; - const GlobalsConfig globals_config = { .bms_ok_latch = &bms_ok_latch, .imd_ok_latch = &imd_ok_latch, diff --git a/firmware/quadruna/BMS/test/test_faults.cpp b/firmware/quadruna/BMS/test/test_faults.cpp index b631968c10..f7616bf929 100644 --- a/firmware/quadruna/BMS/test/test_faults.cpp +++ b/firmware/quadruna/BMS/test/test_faults.cpp @@ -492,7 +492,8 @@ TEST_F(BmsFaultTest, check_state_transition_fault_state_precharge_fault) fake_io_airs_isNegativeClosed_returns(true); app_canRx_Debug_StartCharging_update(false); LetTimePass(210U); - ASSERT_EQ(app_prechargeState_get(), app_stateMachine_getCurrentState()); + ASSERT_EQ(app_prechargeState_get(), app_stateMachine_getCurrentState()) + << "Given " << app_stateMachine_getCurrentState()->name << ", expected to be precharge state"; ASSERT_FALSE(app_canAlerts_BMS_Fault_PrechargeFailure_get()); // 3.8V nominal cell voltage * total # of cells to give estimate of nominal pack voltage diff --git a/firmware/quadruna/BMS/test/test_imd.h b/firmware/quadruna/BMS/test/test_imd.h index ea156f257c..4fa2f6f7f5 100644 --- a/firmware/quadruna/BMS/test/test_imd.h +++ b/firmware/quadruna/BMS/test/test_imd.h @@ -22,14 +22,14 @@ class ImdTest : public testing::Test } }; -static void test_imd_setImdCondition(ImdConditionName condition_name) +static void test_imd_setImdCondition(const ImdConditionName condition_name) { - const float mapping[NUM_OF_IMD_CONDITIONS] = { - [IMD_CONDITION_SHORT_CIRCUIT] = 0.0f, [IMD_CONDITION_NORMAL] = 10.0f, - [IMD_CONDITION_UNDERVOLTAGE_DETECTED] = 20.0f, [IMD_CONDITION_SST] = 30.0f, - [IMD_CONDITION_DEVICE_ERROR] = 40.0f, [IMD_CONDITION_GROUND_FAULT] = 50.0f, + const std::map mapping{ + { IMD_CONDITION_SHORT_CIRCUIT, 0.0f }, { IMD_CONDITION_NORMAL, 10.0f }, + { IMD_CONDITION_UNDERVOLTAGE_DETECTED, 20.0f }, { IMD_CONDITION_SST, 30.0f }, + { IMD_CONDITION_DEVICE_ERROR, 40.0f }, { IMD_CONDITION_GROUND_FAULT, 50.0f } }; - fake_io_imd_getFrequency_returns(mapping[condition_name]); + fake_io_imd_getFrequency_returns(mapping.at(condition_name)); ASSERT_EQ(condition_name, app_imd_getCondition().name); } diff --git a/firmware/quadruna/BMS/test/test_stateMachine.cpp b/firmware/quadruna/BMS/test/test_stateMachine.cpp index ae361e06c6..cc7ebe7fe0 100644 --- a/firmware/quadruna/BMS/test/test_stateMachine.cpp +++ b/firmware/quadruna/BMS/test/test_stateMachine.cpp @@ -371,7 +371,7 @@ TEST_F(BmsStateMachineTest, no_charger_connected_missing_hb_init_state) { SetInitialState(app_initState_get()); fake_io_airs_isNegativeClosed_returns(true); - app_heartbeatMonitor_blockFaults(false); + app_heartbeatMonitor_blockFaults(&hb_monitor, false); app_canRx_VC_Heartbeat_update(false); LetTimePass(500U); diff --git a/firmware/quadruna/CRIT/CMakeLists.txt b/firmware/quadruna/CRIT/CMakeLists.txt index 4d16242af9..858061b3c8 100644 --- a/firmware/quadruna/CRIT/CMakeLists.txt +++ b/firmware/quadruna/CRIT/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB_RECURSE APP_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/src/app/*.c") list(APPEND APP_SRCS "${SHARED_APP_INCLUDE_DIR}/app_stateMachine.c" "${SHARED_APP_INCLUDE_DIR}/app_heartbeatMonitor.c" + "${SHARED_APP_INCLUDE_DIR}/app_heartbeatBoard.c" "${SHARED_APP_INCLUDE_DIR}/app_rangeCheck.c" "${SHARED_APP_INCLUDE_DIR}/app_signal.c" "${SHARED_APP_INCLUDE_DIR}/app_timer.c" diff --git a/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.c b/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.c new file mode 100644 index 0000000000..a2fe796e41 --- /dev/null +++ b/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.c @@ -0,0 +1,37 @@ +#include "app_heartbeatMonitors.h" + +#include "app_canTx.h" +#include "app_canRx.h" +#include "app_canAlerts.h" + +static HeartbeatBoard heartbeat_boards[4] = { + // bms + { .getter = app_canRx_BMS_Heartbeat_get, + .resetter = app_canRx_BMS_Heartbeat_update, + .fault_setter = app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_set, + .fault_getter = app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_get, + .timeout_ms = 200 }, + // vc + { .getter = app_canRx_VC_Heartbeat_get, + .resetter = app_canRx_VC_Heartbeat_update, + .fault_setter = app_canAlerts_CRIT_Fault_MissingVCHeartbeat_set, + .fault_getter = app_canAlerts_CRIT_Fault_MissingVCHeartbeat_get, + .timeout_ms = 200 }, + // rsm + { .getter = app_canRx_RSM_Heartbeat_get, + .resetter = app_canRx_RSM_Heartbeat_update, + .fault_setter = app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_set, + .fault_getter = app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_get, + .timeout_ms = 200 }, + // fsm + { .getter = app_canRx_FSM_Heartbeat_get, + .resetter = app_canRx_FSM_Heartbeat_update, + .fault_setter = app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_set, + .fault_getter = app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_get, + .timeout_ms = 200 } +}; + +HeartbeatMonitor hb_monitor = { .boards = heartbeat_boards, + .board_count = 4, + .block_faults = false, + .own_heartbeat = app_canTx_CRIT_Heartbeat_set }; diff --git a/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.h b/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.h new file mode 100644 index 0000000000..3889cf9413 --- /dev/null +++ b/firmware/quadruna/CRIT/src/app/app_heartbeatMonitors.h @@ -0,0 +1,5 @@ +#pragma once + +#include "app_heartbeatMonitor.h" + +extern HeartbeatMonitor hb_monitor; diff --git a/firmware/quadruna/CRIT/src/app/app_mainState.c b/firmware/quadruna/CRIT/src/app/app_mainState.c index a6b6ec30ab..459a8d4776 100644 --- a/firmware/quadruna/CRIT/src/app/app_mainState.c +++ b/firmware/quadruna/CRIT/src/app/app_mainState.c @@ -1,9 +1,9 @@ #include "app_mainState.h" #include "app_driveMode.h" #include "app_shdnLoop.h" -#include "app_heartbeatMonitor.h" #include "app_leds.h" #include "app_switches.h" +#include "app_heartbeatMonitors.h" static void mainStateRunOnTick100Hz(void) { @@ -13,8 +13,8 @@ static void mainStateRunOnTick100Hz(void) app_shdnLoop_broadcast(); - app_heartbeatMonitor_checkIn(); - app_heartbeatMonitor_broadcastFaults(); + app_heartbeatMonitor_checkIn(&hb_monitor); + app_heartbeatMonitor_broadcastFaults(&hb_monitor); } static void mainStateRunOnEntry(void) {} diff --git a/firmware/quadruna/CRIT/src/tasks.c b/firmware/quadruna/CRIT/src/tasks.c index 7e429b81e7..73acfc704c 100644 --- a/firmware/quadruna/CRIT/src/tasks.c +++ b/firmware/quadruna/CRIT/src/tasks.c @@ -352,52 +352,6 @@ static const Switches switch_config = { .torquevec_switch = &torquevec_switch, }; -// CRIT rellies on BMS, VC, RSM, FSM -static const bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = true, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false -}; - -// heartbeatGetters - get heartbeat signals from other boards -static bool (*const heartbeatGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = app_canRx_RSM_Heartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatUpdaters - update local CAN table with heartbeat status -static void (*const heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = app_canRx_RSM_Heartbeat_update, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatFaultSetters - broadcast heartbeat faults over CAN -static void (*const heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_set, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatFaultGetters - gets fault statuses over CAN -static bool (*const heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - static const CritShdnConfig crit_shdn_pin_config = { .cockpit_estop_gpio = shdn_sen_pin, .inertia_sen_ok_gpio = inertia_sen_pin }; @@ -438,10 +392,6 @@ void tasks_init(void) app_canTx_init(); app_canRx_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_CRIT_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); - app_stateMachine_init(app_mainState_get()); // broadcast commit info diff --git a/firmware/quadruna/CRIT/test/test_critBaseStateMachineTest.h b/firmware/quadruna/CRIT/test/test_critBaseStateMachineTest.h index 9402efc6aa..42f8042113 100644 --- a/firmware/quadruna/CRIT/test/test_critBaseStateMachineTest.h +++ b/firmware/quadruna/CRIT/test/test_critBaseStateMachineTest.h @@ -9,7 +9,7 @@ extern "C" #include "app_canTx.h" #include "app_canRx.h" #include "app_canAlerts.h" -#include "app_heartbeatMonitor.h" +#include "app_heartbeatMonitors.h" #include "app_stateMachine.h" #include "app_canUtils.h" #include "app_utils.h" @@ -29,15 +29,13 @@ class CritBaseStateMachineTest : public BaseStateMachineTest app_canTx_init(); app_canRx_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_CRIT_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); - app_stateMachine_init(app_mainState_get()); - // Disable heartbeat monitor in the nominal case. To use representative heartbeat behavior, // re-enable the heartbeat monitor. - app_heartbeatMonitor_blockFaults(true); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_blockFaults(&hb_monitor, true); + + app_stateMachine_init(app_mainState_get()); + + app_heartbeatMonitor_clearFaults(&hb_monitor); } void TearDown() override @@ -62,48 +60,4 @@ class CritBaseStateMachineTest : public BaseStateMachineTest fake_io_switches_regen_get_reset(); fake_io_switches_torquevec_get_reset(); } - // config to forward can functions to shared heartbeat - // DIM rellies on all boards but itself - const bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = true, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false - }; - - // heartbeatGetters - get heartbeat signals from other boards - bool (*const heartbeatGetters[HEARTBEAT_BOARD_COUNT])() = { [BMS_HEARTBEAT_BOARD] = &app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = &app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = &app_canRx_RSM_Heartbeat_get, - [FSM_HEARTBEAT_BOARD] = &app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL }; - - // heartbeatUpdaters - update local CAN table with heartbeat status - void (*const heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = &app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = &app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = &app_canRx_RSM_Heartbeat_update, - [FSM_HEARTBEAT_BOARD] = &app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatFaultSetters - broadcast heartbeat faults over CAN - void (*const heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_set, - [FSM_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatFaultGetters - gets fault statuses over CAN - bool (*const heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])() = { - [BMS_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingRSMHeartbeat_get, - [FSM_HEARTBEAT_BOARD] = &app_canAlerts_CRIT_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL - }; }; diff --git a/firmware/quadruna/RSM/CMakeLists.txt b/firmware/quadruna/RSM/CMakeLists.txt index 124d8ef208..c3ff0371c0 100644 --- a/firmware/quadruna/RSM/CMakeLists.txt +++ b/firmware/quadruna/RSM/CMakeLists.txt @@ -9,6 +9,7 @@ file(GLOB_RECURSE APP_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/src/app/*.c") list(APPEND APP_SRCS "${SHARED_APP_INCLUDE_DIR}/app_stateMachine.c" "${SHARED_APP_INCLUDE_DIR}/app_heartbeatMonitor.c" + "${SHARED_APP_INCLUDE_DIR}/app_heartbeatBoard.c" "${SHARED_APP_INCLUDE_DIR}/app_rangeCheck.c" "${SHARED_APP_INCLUDE_DIR}/app_signal.c" "${SHARED_APP_INCLUDE_DIR}/app_timer.c" diff --git a/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.c b/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.c new file mode 100644 index 0000000000..dbf0760467 --- /dev/null +++ b/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.c @@ -0,0 +1,31 @@ +#include "app_heartbeatMonitors.h" + +#include "app_canTx.h" +#include "app_canRx.h" +#include "app_canAlerts.h" + +static HeartbeatBoard heartbeat_boards[3] = { + // bms + { .getter = app_canRx_BMS_Heartbeat_get, + .resetter = app_canRx_BMS_Heartbeat_update, + .fault_setter = app_canAlerts_RSM_Fault_MissingBMSHeartbeat_set, + .fault_getter = app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get, + .timeout_ms = 200 }, + // vc + { .getter = app_canRx_VC_Heartbeat_get, + .resetter = app_canRx_VC_Heartbeat_update, + .fault_setter = app_canAlerts_RSM_Fault_MissingVCHeartbeat_set, + .fault_getter = app_canAlerts_RSM_Fault_MissingVCHeartbeat_get, + .timeout_ms = 200 }, + // fsm + { .getter = app_canRx_FSM_Heartbeat_get, + .resetter = app_canRx_FSM_Heartbeat_update, + .fault_setter = app_canAlerts_RSM_Fault_MissingFSMHeartbeat_set, + .fault_getter = app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get, + .timeout_ms = 200 } +}; + +const HeartbeatMonitor hb_monitor = { .boards = heartbeat_boards, + .board_count = 3, + .block_faults = false, + .own_heartbeat = app_canTx_RSM_Heartbeat_set }; diff --git a/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.h b/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.h new file mode 100644 index 0000000000..dfd427ba0c --- /dev/null +++ b/firmware/quadruna/RSM/src/app/app_heartbeatMonitors.h @@ -0,0 +1,5 @@ +#pragma once + +#include "app_heartbeatMonitor.h" + +extern const HeartbeatMonitor hb_monitor; \ No newline at end of file diff --git a/firmware/quadruna/RSM/src/app/app_mainState.c b/firmware/quadruna/RSM/src/app/app_mainState.c index b3292d816b..34689e95eb 100644 --- a/firmware/quadruna/RSM/src/app/app_mainState.c +++ b/firmware/quadruna/RSM/src/app/app_mainState.c @@ -1,22 +1,23 @@ #include -#include "io_fans.h" -#include "io_brake_light.h" + #include "app_canTx.h" #include "app_mainState.h" #include "app_canRx.h" #include "app_coolant.h" #include "app_loadCell.h" #include "app_suspension.h" +#include "app_heartbeatMonitors.h" -#include "app_heartbeatMonitor.h" +#include "io_fans.h" +#include "io_brake_light.h" void mainStateRunOnTick100Hz(void) { app_coolant_broadcast(); app_loadcell_broadcast(); app_suspension_broadcast(); - app_heartbeatMonitor_checkIn(); - app_heartbeatMonitor_broadcastFaults(); + app_heartbeatMonitor_checkIn(&hb_monitor); + app_heartbeatMonitor_broadcastFaults(&hb_monitor); io_brake_light_set(app_canRx_FSM_BrakeActuated_get()); const bool hv_on = app_canRx_BMS_State_get() == BMS_DRIVE_STATE; diff --git a/firmware/quadruna/RSM/src/tasks.c b/firmware/quadruna/RSM/src/tasks.c index 39a420d30e..38187743fc 100644 --- a/firmware/quadruna/RSM/src/tasks.c +++ b/firmware/quadruna/RSM/src/tasks.c @@ -2,13 +2,13 @@ #include "main.h" #include "cmsis_os.h" -#include "app_heartbeatMonitor.h" #include "app_mainState.h" #include "app_canTx.h" #include "app_canRx.h" #include "app_canAlerts.h" #include "app_commitInfo.h" #include "app_coolant.h" +#include "app_heartbeatMonitor.h" #include "io_jsoncan.h" #include "io_canRx.h" @@ -119,49 +119,6 @@ PwmInputFreqOnlyConfig coolant_config = { .htim = &htim3, static const UART debug_uart = { .handle = &huart1 }; -// config for heartbeat monitor -/// RSM rellies on BMS, FSM and VC -bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = false, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false -}; - -// heartbeatGetters - get heartbeat signals from other boards -bool (*heartbeatGetters[HEARTBEAT_BOARD_COUNT])(void) = { [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL }; - -// heartbeatUpdaters - update local CAN table with heartbeat status -void (*heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL }; - -// heartbeatFaultSetters - broadcast heartbeat faults over CAN -void (*heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - -// heartbeatFaultGetters - gets fault statuses over CAN -bool (*heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL -}; - void tasks_preInit(void) { hw_bootup_enableInterruptsForApp(); @@ -197,10 +154,6 @@ void tasks_init(void) io_brake_light_init(&brake_light); app_coolant_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_RSM_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); - app_stateMachine_init(app_mainState_get()); app_canTx_RSM_Hash_set(GIT_COMMIT_HASH); diff --git a/firmware/quadruna/RSM/test/test_faults.cpp b/firmware/quadruna/RSM/test/test_faults.cpp index 535b544d13..814635325c 100644 --- a/firmware/quadruna/RSM/test/test_faults.cpp +++ b/firmware/quadruna/RSM/test/test_faults.cpp @@ -8,90 +8,6 @@ class RsmFaultsTest : public RsmBaseStateMachineTest { }; -TEST_F(RsmFaultsTest, check_state_transition_fault_state_heartbeat_timeout) -{ - // Test that a missing heartbeat will put the RSM into fault state. The RSM monitors the BMS and FSM heartbeat. - app_heartbeatMonitor_blockFaults(false); - // Check in all heartbeats within timeout period - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 10U); - ASSERT_EQ(app_mainState_get(), app_stateMachine_getCurrentState()); - // Heartbeat faults initially present at startup until cleared - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - - app_canRx_VC_Heartbeat_update(true); // Check in VC heartbeat - app_canRx_FSM_Heartbeat_update(true); // check in FSM heartbeat - app_canRx_BMS_Heartbeat_update(true); // check in BMS heartbeat - LetTimePass(10); - ASSERT_EQ(app_mainState_get(), app_stateMachine_getCurrentState()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 10U); - ASSERT_EQ(app_mainState_get(), app_stateMachine_getCurrentState()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - // Fail to check heartbeat, BMS FSM and VC should fault - LetTimePass(20); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(1000); // faulted indefinitely - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - app_canRx_VC_Heartbeat_update(true); // Check in VC - app_canRx_BMS_Heartbeat_update(true); // Check in BMS but no FSM - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 20U); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(1000); // faulted indefinitely and all three faults from timeout independently - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - app_canRx_FSM_Heartbeat_update(true); // check in FSM heartbeat but No VC - app_canRx_BMS_Heartbeat_update(true); // Check in BMS but no VC - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 20U); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(1000); // faulted indefinitely and all three faults from timeout independently - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - - app_canRx_VC_Heartbeat_update(true); // Check in VC heartbeat - app_canRx_FSM_Heartbeat_update(true); // check in FSM heartbeat - app_canRx_BMS_Heartbeat_update(true); // check in BMS heartbeat - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 20U); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(10); - app_canRx_VC_Heartbeat_update(true); // Check in VC heartbeat - app_canRx_FSM_Heartbeat_update(true); // check in FSM heartbeat - - LetTimePass(HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS - 20U); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_FALSE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get()); - - LetTimePass(1000); // faulted indefinitely and all three faults from timeout independently - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get()); - ASSERT_TRUE(app_canAlerts_RSM_Fault_MissingVCHeartbeat_get()); -} - TEST_F(RsmFaultsTest, primary_flow_rate_underflow_sets_fault) { app_canRx_VC_State_update(VC_DRIVE_STATE); diff --git a/firmware/quadruna/RSM/test/test_rsmBaseStateMachineTest.h b/firmware/quadruna/RSM/test/test_rsmBaseStateMachineTest.h index 86a5ff0d16..726cfeccf7 100644 --- a/firmware/quadruna/RSM/test/test_rsmBaseStateMachineTest.h +++ b/firmware/quadruna/RSM/test/test_rsmBaseStateMachineTest.h @@ -13,7 +13,7 @@ extern "C" #include "app_canTx.h" #include "app_canRx.h" #include "app_canAlerts.h" -#include "app_heartbeatMonitor.h" +#include "app_heartbeatMonitors.h" #include "app_stateMachine.h" #include "app_canUtils.h" #include "app_utils.h" @@ -35,14 +35,7 @@ class RsmBaseStateMachineTest : public BaseStateMachineTest app_canRx_init(); app_coolant_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_RSM_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); app_stateMachine_init(app_mainState_get()); - - // Disable heartbeat monitor in the nominal case. To use representative heartbeat behavior, - // re-enable the heartbeat monitor. - app_heartbeatMonitor_blockFaults(true); } void TearDown() override @@ -69,47 +62,4 @@ class RsmBaseStateMachineTest : public BaseStateMachineTest fake_io_brake_light_set_reset(); } - - // config for heartbeat monitor (can funcs and flags) - // RSM rellies on BMS and FSM - bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = true, [RSM_HEARTBEAT_BOARD] = false, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = false - }; - - // heartbeatGetters - get heartbeat signals from other boards - bool (*heartbeatGetters[HEARTBEAT_BOARD_COUNT])() = { [BMS_HEARTBEAT_BOARD] = &app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = &app_canRx_VC_Heartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = &app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL }; - - // heartbeatUpdaters - update local CAN table with heartbeat status - void (*heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { [BMS_HEARTBEAT_BOARD] = &app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = &app_canRx_VC_Heartbeat_update, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = &app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL }; - - // heartbeatFaultSetters - broadcast heartbeat faults over CAN - void (*heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingVCHeartbeat_set, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL - }; - - // heartbeatFaultGetters - gets fault statuses over CAN - bool (*heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])() = { - [BMS_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingVCHeartbeat_get, - [RSM_HEARTBEAT_BOARD] = NULL, - [FSM_HEARTBEAT_BOARD] = &app_canAlerts_RSM_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = NULL - }; }; \ No newline at end of file diff --git a/firmware/quadruna/VC/CMakeLists.txt b/firmware/quadruna/VC/CMakeLists.txt index 26fcce614b..d7e3c5396c 100644 --- a/firmware/quadruna/VC/CMakeLists.txt +++ b/firmware/quadruna/VC/CMakeLists.txt @@ -8,6 +8,7 @@ set(LINKER_SCRIPT "${LINKER_DIR}/stm32h733vgtx/stm32h733vgtx_app.ld") file(GLOB_RECURSE APP_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/src/app/*.c") list(APPEND APP_SRCS "${SHARED_APP_INCLUDE_DIR}/app_heartbeatMonitor.c" + "${SHARED_APP_INCLUDE_DIR}/app_heartbeatBoard.c" "${SHARED_APP_INCLUDE_DIR}/app_stateMachine.c" "${SHARED_APP_INCLUDE_DIR}/app_timer.c" "${SHARED_APP_INCLUDE_DIR}/app_shdnLoopNode.c" diff --git a/firmware/quadruna/VC/src/app/app_heartbeatMonitors.c b/firmware/quadruna/VC/src/app/app_heartbeatMonitors.c new file mode 100644 index 0000000000..d351029628 --- /dev/null +++ b/firmware/quadruna/VC/src/app/app_heartbeatMonitors.c @@ -0,0 +1,38 @@ +#include "app_heartbeatMonitors.h" + +// CAN +#include "app_canRx.h" +#include "app_canTx.h" +#include "app_canAlerts.h" + +static HeartbeatBoard heartbeat_boards[4] = { + // bms + { .getter = app_canRx_BMS_Heartbeat_get, + .resetter = app_canRx_BMS_Heartbeat_update, + .fault_setter = app_canAlerts_VC_Fault_MissingBMSHeartbeat_set, + .fault_getter = app_canAlerts_VC_Fault_MissingBMSHeartbeat_get, + .timeout_ms = 200 }, + // rsm + { .getter = app_canRx_RSM_Heartbeat_get, + .resetter = app_canRx_RSM_Heartbeat_update, + .fault_setter = app_canAlerts_VC_Fault_MissingRSMHeartbeat_set, + .fault_getter = app_canAlerts_VC_Fault_MissingRSMHeartbeat_get, + .timeout_ms = 200 }, + // fsm + { .getter = app_canRx_FSM_Heartbeat_get, + .resetter = app_canRx_FSM_Heartbeat_update, + .fault_setter = app_canAlerts_VC_Fault_MissingFSMHeartbeat_set, + .fault_getter = app_canAlerts_VC_Fault_MissingFSMHeartbeat_get, + .timeout_ms = 200 }, + // crit + { .getter = app_canRx_CRIT_Heartbeat_get, + .resetter = app_canRx_CRIT_Heartbeat_update, + .fault_setter = app_canAlerts_VC_Fault_MissingCRITHeartbeat_set, + .fault_getter = app_canAlerts_VC_Fault_MissingCRITHeartbeat_get, + .timeout_ms = 200 } +}; + +const HeartbeatMonitor hb_monitor = { .boards = heartbeat_boards, + .board_count = 4, + .block_faults = false, + .own_heartbeat = app_canTx_VC_Heartbeat_set }; diff --git a/firmware/quadruna/VC/src/app/app_heartbeatMonitors.h b/firmware/quadruna/VC/src/app/app_heartbeatMonitors.h new file mode 100644 index 0000000000..6d02b47e45 --- /dev/null +++ b/firmware/quadruna/VC/src/app/app_heartbeatMonitors.h @@ -0,0 +1,5 @@ +#pragma once + +#include "app_heartbeatMonitor.h" + +extern const HeartbeatMonitor hb_monitor; diff --git a/firmware/quadruna/VC/src/app/states/app_allStates.c b/firmware/quadruna/VC/src/app/states/app_allStates.c index 203b0558fa..a237d930c9 100644 --- a/firmware/quadruna/VC/src/app/states/app_allStates.c +++ b/firmware/quadruna/VC/src/app/states/app_allStates.c @@ -17,6 +17,8 @@ #include "io_canLogging.h" #include "io_pcm.h" +#include + #define IGNORE_HEARTBEAT_CYCLES 3U static uint16_t heartbeat_cycles = 0; @@ -48,12 +50,12 @@ void app_allStates_runOnTick100Hz(void) app_canTx_VC_ImuAccelerationZ_set(lin_accel_z); } - app_heartbeatMonitor_checkIn(); + app_heartbeatMonitor_checkIn(&hb_monitor); if (heartbeat_cycles <= IGNORE_HEARTBEAT_CYCLES) // TODO make this part of the heartbeat monitor heartbeat_cycles++; else - app_heartbeatMonitor_broadcastFaults(); + app_heartbeatMonitor_broadcastFaults(&hb_monitor); io_sbgEllipse_handleLogs(); app_sbgEllipse_broadcast(); diff --git a/firmware/quadruna/VC/src/tasks.c b/firmware/quadruna/VC/src/tasks.c index 223935cd1d..ca820a4d26 100644 --- a/firmware/quadruna/VC/src/tasks.c +++ b/firmware/quadruna/VC/src/tasks.c @@ -1,18 +1,15 @@ #include "tasks.h" #include "main.h" #include "cmsis_os.h" -#include "string.h" #include "shared.pb.h" #include "states/app_allStates.h" #include "states/app_initState.h" -#include "app_heartbeatMonitor.h" #include "app_canTx.h" #include "app_canRx.h" #include "app_canAlerts.h" #include "app_canDataCapture.h" #include "app_commitInfo.h" -#include "app_powerManager.h" #include "app_faultCheck.h" #include "io_jsoncan.h" @@ -263,51 +260,6 @@ static const UART modem2G4_uart = { .handle = &huart3 }; static const UART modem900_uart = { .handle = &huart1 }; static const Modem modem = { .modem2_4G = &modem2G4_uart, .modem900M = &modem900_uart }; -// config for heartbeat monitor (can funcs and flags) -// VC relies on FSM, RSM, BMS, CRIT -const bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = false, [RSM_HEARTBEAT_BOARD] = true, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = true -}; - -// heartbeatGetters - get heartbeat signals from other boards -bool (*const heartbeatGetters[HEARTBEAT_BOARD_COUNT])(void) = { [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canRx_RSM_Heartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canRx_CRIT_Heartbeat_get }; - -// heartbeatUpdaters - update local CAN table with heartbeat status -void (*const heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canRx_RSM_Heartbeat_update, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canRx_CRIT_Heartbeat_update -}; - -// heartbeatFaultSetters - broadcast heartbeat faults over CAN -void (*const heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingRSMHeartbeat_set, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingCRITHeartbeat_set -}; - -// heartbeatFaultGetters - gets fault statuses over CAN -bool (*const heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])(void) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingRSMHeartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingCRITHeartbeat_get -}; - void tasks_preInit(void) { hw_bootup_enableInterruptsForApp(); @@ -383,9 +335,6 @@ void tasks_init(void) app_canAlerts_VC_Warning_HighNumberOfCanDataLogs_set(io_canLogging_getCurrentLog() > HIGH_NUMBER_OF_LOGS_THRESHOLD); app_canAlerts_VC_Warning_CanLoggingSdCardNotPresent_set(!sd_card_present); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_VC_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); app_stateMachine_init(app_initState_get()); io_telemMessage_init(&modem); diff --git a/firmware/quadruna/VC/test/test_stateMachine.cpp b/firmware/quadruna/VC/test/test_stateMachine.cpp index fec7506251..3a7d2de8bd 100644 --- a/firmware/quadruna/VC/test/test_stateMachine.cpp +++ b/firmware/quadruna/VC/test/test_stateMachine.cpp @@ -6,7 +6,7 @@ class VCStateMachineTest : public VcBaseStateMachineTest void TestFaultBlocksDrive(const std::function &set_fault, const std::function &clear_fault) { SetInitialState(app_driveState_get()); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); // Set the CRIT start switch to on, and the BMS to drive state, to prevent state transitions in // the drive state. @@ -46,7 +46,7 @@ TEST_F(VCStateMachineTest, test_SetStateToDrive) TEST_F(VCStateMachineTest, check_init_transitions_to_drive_if_conditions_met_and_start_switch_pulled_up) { - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); app_canRx_CRIT_StartSwitch_update(SWITCH_OFF); app_canRx_CRIT_StartSwitch_update(SWITCH_ON); @@ -80,7 +80,7 @@ TEST_F(VCStateMachineTest, check_init_state_is_broadcasted_over_can) TEST_F(VCStateMachineTest, check_state_transition_from_init_to_inverter_on) { SetInitialState(app_initState_get()); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); app_canRx_BMS_State_update(BMS_DRIVE_STATE); LetTimePass(1000); EXPECT_EQ(VC_INVERTER_ON_STATE, app_canTx_VC_State_get()); @@ -106,7 +106,7 @@ TEST_F(VCStateMachineTest, check_inverter_on_state_is_broadcasted_over_can) TEST_F(VCStateMachineTest, disable_inverters_in_init_state) { SetInitialState(app_initState_get()); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); app_canRx_BMS_State_update(BMS_DRIVE_STATE); LetTimePass(1000); // Transitioning from init state to inverter on state as inverters have been turned on @@ -149,7 +149,7 @@ TEST_F(VCStateMachineTest, check_if_buzzer_stays_on_for_two_seconds_only_after_e { VcBaseStateMachineTest::SetUp(); SetInitialState(state); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); if (app_canTx_VC_State_get() == VC_DRIVE_STATE) { diff --git a/firmware/quadruna/VC/test/test_vcBaseStateMachineTest.h b/firmware/quadruna/VC/test/test_vcBaseStateMachineTest.h index ad6cf7c905..5e94015d27 100644 --- a/firmware/quadruna/VC/test/test_vcBaseStateMachineTest.h +++ b/firmware/quadruna/VC/test/test_vcBaseStateMachineTest.h @@ -1,13 +1,10 @@ #include #include "test_baseStateMachineTest.h" -#include "fake_io_time.hpp" #include "fake_io_lowVoltageBattery.hpp" #include "fake_io_efuse.hpp" -#include "fake_io_led.hpp" #include "fake_io_sbgEllipse.hpp" #include "fake_io_pcm.hpp" -#include "fake_io_canLogging.hpp" extern "C" { @@ -26,6 +23,7 @@ extern "C" #include "app_efuse.h" #include "app_faultCheck.h" #include "app_regen.h" +#include "app_heartbeatMonitors.h" } // Test fixture definition for any test requiring the state machine. Can also be used for non-state machine related @@ -41,17 +39,9 @@ class VcBaseStateMachineTest : public BaseStateMachineTest app_canTx_init(); app_canRx_init(); - app_heartbeatMonitor_init( - heartbeatMonitorChecklist, heartbeatGetters, heartbeatUpdaters, &app_canTx_VC_Heartbeat_set, - heartbeatFaultSetters, heartbeatFaultGetters); - // app_globals_init(&globals_config); - // Default to starting the state machine in the `init` state app_stateMachine_init(app_initState_get()); - // Disable heartbeat monitor in the nominal case. To use representative heartbeat behavior, - // re-enable the heartbeat monitor. - app_heartbeatMonitor_blockFaults(true); app_faultCheck_init(); memset(&fake_sensor_data, 0U, sizeof(fake_sensor_data)); @@ -98,7 +88,7 @@ class VcBaseStateMachineTest : public BaseStateMachineTest app_canRx_BMS_State_update(BMS_DRIVE_STATE); app_canRx_FSM_BrakeActuated_update(true); SetInitialState(app_driveState_get()); - app_heartbeatMonitor_clearFaults(); + app_heartbeatMonitor_clearFaults(&hb_monitor); } // configs for efuse messages over can @@ -107,54 +97,5 @@ class VcBaseStateMachineTest : public BaseStateMachineTest return std::vector{ app_initState_get(), app_driveState_get() }; } - // config for heartbeat monitor (can funcs and flags) - // VC relies on FSM, RSM, BMS, CRIT - bool heartbeatMonitorChecklist[HEARTBEAT_BOARD_COUNT] = { - [BMS_HEARTBEAT_BOARD] = true, [VC_HEARTBEAT_BOARD] = false, [RSM_HEARTBEAT_BOARD] = true, - [FSM_HEARTBEAT_BOARD] = true, [DIM_HEARTBEAT_BOARD] = false, [CRIT_HEARTBEAT_BOARD] = true - }; - - // heartbeatGetters - get heartbeat signals from other boards - bool (*heartbeatGetters[HEARTBEAT_BOARD_COUNT])() = { [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_get, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canRx_CRIT_Heartbeat_get }; - - // heartbeatUpdaters - update local CAN table with heartbeat status - void (*heartbeatUpdaters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canRx_BMS_Heartbeat_update, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canRx_RSM_Heartbeat_update, - [FSM_HEARTBEAT_BOARD] = app_canRx_FSM_Heartbeat_update, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canRx_CRIT_Heartbeat_update - }; - - // heartbeatFaultSetters - broadcast heartbeat faults over CAN - void (*heartbeatFaultSetters[HEARTBEAT_BOARD_COUNT])(bool) = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingBMSHeartbeat_set, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingRSMHeartbeat_set, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingFSMHeartbeat_set, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingCRITHeartbeat_set - }; - - // heartbeatFaultGetters - gets fault statuses over CAN - bool (*heartbeatFaultGetters[HEARTBEAT_BOARD_COUNT])() = { - [BMS_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingBMSHeartbeat_get, - [VC_HEARTBEAT_BOARD] = NULL, - [RSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingRSMHeartbeat_get, - [FSM_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingFSMHeartbeat_get, - [DIM_HEARTBEAT_BOARD] = NULL, - [CRIT_HEARTBEAT_BOARD] = app_canAlerts_VC_Fault_MissingCRITHeartbeat_get - }; - - // const GlobalsConfig globals_config = { - // .a = 0 - // }; - SensorData fake_sensor_data; }; \ No newline at end of file diff --git a/firmware/shared/CMakeLists.txt b/firmware/shared/CMakeLists.txt index b591625f47..21f273d9d3 100644 --- a/firmware/shared/CMakeLists.txt +++ b/firmware/shared/CMakeLists.txt @@ -1,13 +1,12 @@ -file(GLOB_RECURSE SHARED_APP_SRCS "${SHARED_APP_INCLUDE_DIR}/*.c") -file(GLOB_RECURSE SHARED_IO_SRCS "${SHARED_IO_INCLUDE_DIR}/*.c") -file(GLOB_RECURSE SHARED_HW_SRCS "${SHARED_HW_INCLUDE_DIR}/*.c") +message("") +message("⚙️ Configuring Shared") IF("${TARGET}" STREQUAL "test") + # fakelib list(APPEND HDRS_TO_FAKE "${SHARED_IO_INCLUDE_DIR}/io_time.h" "${SHARED_IO_INCLUDE_DIR}/io_led.h" "${SHARED_IO_INCLUDE_DIR}/io_switch.h" - "${SHARED_IO_INCLUDE_DIR}/io_time.h" "${SHARED_IO_INCLUDE_DIR}/io_mechanicalLoad.h" ) create_fake_library( @@ -15,27 +14,17 @@ IF("${TARGET}" STREQUAL "test") "${HDRS_TO_FAKE}" ) - jsoncan_sources( - "shared" - "${CMAKE_CURRENT_BINARY_DIR}" - FALSE - "quadruna" - ) - add_library("shared_can" STATIC "${CAN_SRCS}") - target_include_directories("shared_can" PUBLIC "${CAN_INCLUDE_DIRS}") + # jsoncan lib + jsoncan_library("shared" "quadruna" "${CMAKE_CURRENT_BINARY_DIR}") + file(GLOB_RECURSE SHARED_TEST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/test/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp" ) - set(SHARED_TEST_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") - set(SRCS ${SHARED_TEST_SRCS} ${SHARED_APP_SRCS}) - set(INCLUDE_DIRS ${SHARED_APP_INCLUDE_DIR} ${SHARED_TEST_INCLUDE_DIR} ${FFF_DIR} ${SHARED_TEST_UTILS_INCLUDE_DIRS}) - compile_gtest_executable( - "shared_test" - "${SRCS}" - "${INCLUDE_DIRS}" - ) - target_link_libraries("shared_test" "shared_can" "shared_fakes") + set(INCLUDE_DIRS ${SHARED_APP_INCLUDE_DIR} ${FFF_DIR} ${SHARED_TEST_UTILS_INCLUDE_DIRS}) + compile_gtest_executable("shared_test" "${SRCS}" "${INCLUDE_DIRS}") + + target_link_libraries("shared_test" "quadruna_shared_jsoncan" "shared_fakes") ENDIF() \ No newline at end of file diff --git a/firmware/shared/src/app/app_heartbeatBoard.c b/firmware/shared/src/app/app_heartbeatBoard.c new file mode 100644 index 0000000000..e3f6dd900b --- /dev/null +++ b/firmware/shared/src/app/app_heartbeatBoard.c @@ -0,0 +1,49 @@ +#include +#include +#include "app_heartbeatBoard.h" + +void app_heartbeatBoard_init(HeartbeatBoard *hb) +{ + assert( + hb->getter != NULL && hb->resetter != NULL && hb->fault_setter != NULL && hb->fault_getter != NULL && + hb->timeout_ms > 0); + hb->heartbeat_checked_in = false; + hb->status = false; + app_timer_init(&hb->timer, hb->timeout_ms); + hb->timer.state = TIMER_STATE_EXPIRED; + hb->fault_setter(true); +} + +/** + * USES + * - getters - getter functions for heartbeats + * - resetters - update functions for heartbeats on local can tables + * - self_checkin - update function for current board + */ +void app_heartbeatBoard_checkIn(HeartbeatBoard *hb) +{ + // check in, and reset board on local CAN table + const bool board_status_good = hb->getter(); + hb->heartbeat_checked_in = board_status_good; + const TimerState state = app_timer_runIfCondition(&hb->timer, !board_status_good); + + hb->status = board_status_good || state == TIMER_STATE_RUNNING; + hb->resetter(false); // reset the CAN table so that it has to be checked in again +} + +void app_heartbeatBoard_broadcastFaults(const HeartbeatBoard *hb) +{ + hb->fault_setter(!hb->status); +} + +bool app_heartbeatBoard_isSendingMissingHeartbeatFault(const HeartbeatBoard *hb) +{ + return hb->fault_getter(); +} + +#ifdef TARGET_TEST +void app_heartbeatBoard_clearFaults(const HeartbeatBoard *hb) +{ + hb->fault_setter(false); +} +#endif diff --git a/firmware/shared/src/app/app_heartbeatBoard.h b/firmware/shared/src/app/app_heartbeatBoard.h new file mode 100644 index 0000000000..fbf32405fa --- /dev/null +++ b/firmware/shared/src/app/app_heartbeatBoard.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include "app_timer.h" + +typedef struct +{ + // gives if the heartbeat is checked in. + // unmonitored heartbeats should be false. + bool heartbeat_checked_in; + // gives if the heartbeat is in a valid state. + // a flag in status is true under three conditions + // 1) if it has been checked in + // 2) it was not on our list of heartbeats to check + // 3) if the heartbeat_checked_in is true but the timeout has not been elapsed. + bool status; + TimerChannel timer; + + // PUBLIC ELEMENTS + const uint8_t timeout_ms; + + // HEARTBEAT CAN SIGNALS + // getters for heartbeats on the CAN table + bool (*const getter)(void); + // resetters on the local CAN table for other heartbeats + // WARNING: only pass false into this function. idk how to make a closure, so we can't make this only return false + void (*const resetter)(bool); + + // FAULT SETGET + // fault broadcasters for each board's heartbeat from this board + void (*const fault_setter)(bool); + // fault getters for each board's heartbeat from this board + bool (*const fault_getter)(void); +} HeartbeatBoard; + +void app_heartbeatBoard_init(HeartbeatBoard *hb); + +/** + * Populates heartbeats_checked_in + */ +void app_heartbeatBoard_checkIn(HeartbeatBoard *hb); + +/** + * Gets state to broadcast via can, and can callbacks to use to broadcast + */ +void app_heartbeatBoard_broadcastFaults(const HeartbeatBoard *hb); + +/** + * @return Whether the heartbeat monitor for the current board has detected any fault + */ +bool app_heartbeatBoard_isSendingMissingHeartbeatFault(const HeartbeatBoard *hb); + +#ifdef TARGET_TEST +/** + * Resets faults as to report as false, useful for test environments + */ +void app_heartbeatBoard_clearFaults(const HeartbeatBoard *hb); +#endif diff --git a/firmware/shared/src/app/app_heartbeatMonitor.c b/firmware/shared/src/app/app_heartbeatMonitor.c index 3d8ef99d78..86746fe8de 100644 --- a/firmware/shared/src/app/app_heartbeatMonitor.c +++ b/firmware/shared/src/app/app_heartbeatMonitor.c @@ -1,135 +1,36 @@ #include "app_heartbeatMonitor.h" -#include "app_heartbeatBoardsEnum.h" -#include "app_timer.h" -#include -#include -static TimerChannel timers[HEARTBEAT_BOARD_COUNT]; - -static HeartbeatMonitor hb_monitor; - -void app_heartbeatMonitor_init( - const bool boards_to_check[HEARTBEAT_BOARD_COUNT], - bool (*const getters[HEARTBEAT_BOARD_COUNT])(void), - void (*const updaters[HEARTBEAT_BOARD_COUNT])(bool), - void (*self_checkiner)(bool), - void (*const fault_setters[HEARTBEAT_BOARD_COUNT])(bool), - bool (*const fault_getters[HEARTBEAT_BOARD_COUNT])(void)) +void app_heartbeatMonitor_checkIn(const HeartbeatMonitor *hbm) { - hb_monitor.self_checkin = self_checkiner; - hb_monitor.block_faults = false; - - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - hb_monitor.heartbeats_checked_in[board] = false; - hb_monitor.status[board] = false; - hb_monitor.is_watching_heartbeat_for[board] = boards_to_check[board]; - hb_monitor.getters[board] = getters[board]; - hb_monitor.resetters[board] = updaters[board]; - hb_monitor.fault_setters[board] = fault_setters[board]; - hb_monitor.fault_getters[board] = fault_getters[board]; - - app_timer_init(&timers[board], HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS); // TODO board specific timeout - // Initialize timer to expired as to cause fault to be present on power-on (missing heartbeat timeout period - // initially expired) - timers[board].state = TIMER_STATE_EXPIRED; - - if (!hb_monitor.is_watching_heartbeat_for[board]) - continue; - assert((hb_monitor.fault_setters[board] != NULL)); - // By default, set fault for all boards to true on start-up - hb_monitor.fault_setters[board](true); - } + hbm->own_heartbeat(true); + for (int i = 0; i < hbm->board_count; i++) + app_heartbeatBoard_checkIn(&hbm->boards[i]); } -/** - * USES - * - getters - getter functions for heartbeats - * - resetters - update functions for heartbeats on local can tables - * - self_checkin - update function for current board - */ -void app_heartbeatMonitor_checkIn(void) +void app_heartbeatMonitor_broadcastFaults(const HeartbeatMonitor *hbm) { - // check in current heartbeat - (hb_monitor.self_checkin)(true); - - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - // if boards heartbeat returned - if (!hb_monitor.is_watching_heartbeat_for[board]) - { - hb_monitor.status[board] = true; - continue; - } - assert(hb_monitor.getters[board] != NULL && hb_monitor.resetters[board] != NULL); - - // check in, and reset board on local CAN table - const bool board_status_good = hb_monitor.getters[board](); - hb_monitor.heartbeats_checked_in[board] = board_status_good; - const TimerState state = app_timer_runIfCondition(&timers[board], !board_status_good); - - hb_monitor.status[board] = board_status_good || state == TIMER_STATE_RUNNING; - hb_monitor.resetters[board](false); // reset the CAN table so that it has to be checked in again - } -} - -void app_heartbeatMonitor_broadcastFaults(void) -{ - // Don't check if blocking faults. - if (hb_monitor.block_faults) - { + if (hbm->block_faults) return; - } - - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - if (!hb_monitor.is_watching_heartbeat_for[board]) - continue; - assert((hb_monitor.fault_setters[board] != NULL)); - const bool board_has_fault = !hb_monitor.status[board]; - hb_monitor.fault_setters[board](board_has_fault); - } + for (int i = 0; i < hbm->board_count; i++) + app_heartbeatBoard_broadcastFaults(&hbm->boards[i]); } -bool app_heartbeatMonitor_isSendingMissingHeartbeatFault(void) +bool app_heartbeatMonitor_isSendingMissingHeartbeatFault(const HeartbeatMonitor *hbm) { - if (hb_monitor.block_faults) - { - return false; - } - - bool res = false; - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - // TODO switch this to checking if hb_monitor.is_watching_heartbeat_for[board] is true - if (hb_monitor.fault_getters[board] == NULL) - continue; - res |= (*hb_monitor.fault_getters[board])(); - } - - return res; + for (int i = 0; i < hbm->board_count; i++) + if (app_heartbeatBoard_isSendingMissingHeartbeatFault(&hbm->boards[i])) + return true; + return false; } #ifdef TARGET_TEST -void app_heartbeatMonitor_blockFaults(bool block_faults) +void app_heartbeatMonitor_clearFaults(const HeartbeatMonitor *hbm) { - hb_monitor.block_faults = block_faults; + for (int i = 0; i < hbm->board_count; i++) + app_heartbeatBoard_clearFaults(&hbm->boards[i]); } - -void app_heartbeatMonitor_clearFaults(void) -{ - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - if (!hb_monitor.is_watching_heartbeat_for[board]) - continue; - assert((hb_monitor.fault_setters[board] != NULL)); - - hb_monitor.fault_setters[board](false); - } -} - -HeartbeatMonitor *app_heartbeatMonitor_get(void) +void app_heartbeatMonitor_blockFaults(HeartbeatMonitor *hbm, bool block_faults_init) { - return &hb_monitor; + hbm->block_faults = block_faults_init; } #endif diff --git a/firmware/shared/src/app/app_heartbeatMonitor.h b/firmware/shared/src/app/app_heartbeatMonitor.h index 338ac2104e..296b0c0190 100644 --- a/firmware/shared/src/app/app_heartbeatMonitor.h +++ b/firmware/shared/src/app/app_heartbeatMonitor.h @@ -1,86 +1,22 @@ #pragma once - #include -#include -#include "app_heartbeatBoardsEnum.h" - -#define HEARTBEAT_MONITOR_TIMEOUT_PERIOD_MS 200U +#include "app_heartbeatBoard.h" typedef struct { - // CONFIG SETTINGS - - // self_checkin for own heartbeat - void (*self_checkin)(bool); - // Override to block heartbeat faults during tests. - bool block_faults; - - // determines if the heartbeat monitor should check in on a board - bool is_watching_heartbeat_for[HEARTBEAT_BOARD_COUNT]; - - // gives if the heartbeat is checked in. - // unmonitored heartbeats should be false. - bool heartbeats_checked_in[HEARTBEAT_BOARD_COUNT]; - // gives if the heartbeat is in a valid state. - // a flag in status is true under three conditions - // 1) if it has been checked in - // 2) it was not on our list of heartbeats to check - // 3) if the heartbeat_checked_in is true but the timeout has not been elapsed. - bool status[HEARTBEAT_BOARD_COUNT]; - - // HEARTBEAT CAN SIGNALS - - // getters for heartbeats on the CAN table - bool (*getters[HEARTBEAT_BOARD_COUNT])(void); - // resetters on the local CAN table for other heartbeats - // WARNING: only pass false into this function. idk how to make a closure, so we can't make this only return false - void (*resetters[HEARTBEAT_BOARD_COUNT])(bool); - - // FAULT SETGET - - // fault broadcasters for each board's heartbeat from this board - void (*fault_setters[HEARTBEAT_BOARD_COUNT])(bool); - // fault getters for each board's heartbeat from this board - bool (*fault_getters[HEARTBEAT_BOARD_COUNT])(void); + HeartbeatBoard *boards; + uint8_t board_count; + bool block_faults; + void (*own_heartbeat)(bool); } HeartbeatMonitor; -void app_heartbeatMonitor_init( - const bool boards_to_check[HEARTBEAT_BOARD_COUNT], - bool (*const getters[HEARTBEAT_BOARD_COUNT])(), - void (*const updaters[HEARTBEAT_BOARD_COUNT])(bool), - void (*self_checkiner)(bool), - void (*const fault_setters[HEARTBEAT_BOARD_COUNT])(bool), - bool (*const fault_getters[HEARTBEAT_BOARD_COUNT])()); - -/** - * Populates heartbeats_checked_in - */ -void app_heartbeatMonitor_checkIn(void); - -/** - * Gets state to broadcast via can, and can callbacks to use to broadcast - */ -void app_heartbeatMonitor_broadcastFaults(void); - -/** - * @return Whether the heartbeat monitor for the current board has detected any fault - */ -bool app_heartbeatMonitor_isSendingMissingHeartbeatFault(void); +void app_heartbeatMonitor_checkIn(const HeartbeatMonitor *hbm); -/** - * Blocks faults from being reported in app_heartbeatMonitor_isSendingMissingHeartbeatFault - * @param block_faults Whether to block faults - */ -void app_heartbeatMonitor_blockFaults(bool block_faults); +void app_heartbeatMonitor_broadcastFaults(const HeartbeatMonitor *hbm); -/** - * Resets faults as to report as false, useful for test environments - */ -void app_heartbeatMonitor_clearFaults(void); +bool app_heartbeatMonitor_isSendingMissingHeartbeatFault(const HeartbeatMonitor *hbm); #ifdef TARGET_TEST -/** - * Get reference to heartbeat monitor, for testing. - */ -HeartbeatMonitor *app_heartbeatMonitor_get(void); -#endif +void app_heartbeatMonitor_clearFaults(const HeartbeatMonitor *hbm); +void app_heartbeatMonitor_blockFaults(HeartbeatMonitor *hbm, bool block_faults_init); +#endif \ No newline at end of file diff --git a/firmware/shared/test/app_heartbeatBoardsEnum.h b/firmware/shared/test/app_heartbeatBoardsEnum.h deleted file mode 100644 index 2db7b78db2..0000000000 --- a/firmware/shared/test/app_heartbeatBoardsEnum.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// Car agnostic board enum for shared tests -typedef enum -{ - A_HEARTBEAT_BOARD, - B_HEARTBEAT_BOARD, - C_HEARTBEAT_BOARD, - D_HEARTBEAT_BOARD, - E_HEARTBEAT_BOARD, - HEARTBEAT_BOARD_COUNT -} HeartbeatBoards; \ No newline at end of file diff --git a/firmware/shared/test/test_heartbeatMonitor.cpp b/firmware/shared/test/test_heartbeatMonitor.cpp index 340e235e61..0b0bd8ddd5 100644 --- a/firmware/shared/test/test_heartbeatMonitor.cpp +++ b/firmware/shared/test/test_heartbeatMonitor.cpp @@ -1,324 +1,85 @@ #include - #include "fake_io_time.hpp" - extern "C" { -#include "app_heartbeatBoardsEnum.h" -#include "app_heartbeatMonitor.h" +#include "app_heartbeatBoard.h" } // fake can states -bool E_fake_can_fault_state = false; -bool A_fake_can_fault_state = false; - -bool E_fake_can_heartbeat_state = false; -bool A_fake_can_heartbeat_state = false; -bool D_fake_can_heartbeat_state = false; - -// fake can methods -void E_fakeCanHeartbeatUpdater(bool val) +bool MOCK_BOARD_CAN_FAULT = false; +bool MOCK_CAN_HEARTBEAT_STATE = false; +uint8_t timeout_ms = 200; +HeartbeatBoard a_hbmonitor = { + .timeout_ms = timeout_ms, + .getter = []() { return MOCK_CAN_HEARTBEAT_STATE; }, + .resetter = [](bool v) { MOCK_CAN_HEARTBEAT_STATE = v; }, + .fault_setter = [](bool v) { MOCK_BOARD_CAN_FAULT = v; }, + .fault_getter = []() { return MOCK_BOARD_CAN_FAULT; }, +}; + +class HeartbeatMonitorTest : public testing::Test { - E_fake_can_heartbeat_state = val; -} - -void A_fakeCanHeartbeatUpdater(bool val) -{ - A_fake_can_heartbeat_state = val; -} - -void D_fakeCanHeartbeatSetter(bool val) -{ - D_fake_can_heartbeat_state = val; -} + protected: + void SetUp() override { app_heartbeatBoard_init(&a_hbmonitor); } +}; -void E_fakeCanFaultSetter(bool val) +TEST_F(HeartbeatMonitorTest, test_create) { - E_fake_can_fault_state = val; -} - -void A_fakeCanFaultSetter(bool val) -{ - A_fake_can_fault_state = val; -} - -bool E_fakeCanFaultGetter() -{ - return E_fake_can_fault_state; -} - -bool A_fakeCanFaultGetter() -{ - return A_fake_can_fault_state; -} - -bool E_fakeCanHeartbeatGetter() -{ - return E_fake_can_heartbeat_state; -} - -bool A_fakeCanHeartbeatGetter() -{ - return A_fake_can_heartbeat_state; -} - -// callback config structs to be passed in to methods later on -void (*fault_can_setters[HEARTBEAT_BOARD_COUNT])(bool) = { [A_HEARTBEAT_BOARD] = &A_fakeCanFaultSetter, - [B_HEARTBEAT_BOARD] = NULL, - [C_HEARTBEAT_BOARD] = NULL, - [D_HEARTBEAT_BOARD] = NULL, - [E_HEARTBEAT_BOARD] = &E_fakeCanFaultSetter }; - -void (*heartbeat_can_updaters[HEARTBEAT_BOARD_COUNT])(bool) = { [A_HEARTBEAT_BOARD] = &A_fakeCanHeartbeatUpdater, - [B_HEARTBEAT_BOARD] = NULL, - [C_HEARTBEAT_BOARD] = NULL, - [D_HEARTBEAT_BOARD] = NULL, - [E_HEARTBEAT_BOARD] = &E_fakeCanHeartbeatUpdater }; - -bool (*heartbeat_can_getters[HEARTBEAT_BOARD_COUNT])() = { [A_HEARTBEAT_BOARD] = &A_fakeCanHeartbeatGetter, - [B_HEARTBEAT_BOARD] = NULL, - [C_HEARTBEAT_BOARD] = NULL, - [D_HEARTBEAT_BOARD] = NULL, - [E_HEARTBEAT_BOARD] = &E_fakeCanHeartbeatGetter }; - -bool (*fault_can_getters[HEARTBEAT_BOARD_COUNT])() = { [A_HEARTBEAT_BOARD] = &A_fakeCanFaultGetter, - [B_HEARTBEAT_BOARD] = NULL, - [C_HEARTBEAT_BOARD] = NULL, - [D_HEARTBEAT_BOARD] = NULL, - [E_HEARTBEAT_BOARD] = &E_fakeCanFaultGetter }; - -TEST(HeartbeatMonitor, test_check_faults) -{ - /* test check faults */ - // this hypothetical (fake) board only checks the A and E, and is the D - bool heartbeats_to_check[HEARTBEAT_BOARD_COUNT]; - for (bool &board : heartbeats_to_check) - { - board = false; - } - - heartbeats_to_check[A_HEARTBEAT_BOARD] = true; - heartbeats_to_check[E_HEARTBEAT_BOARD] = true; - - app_heartbeatMonitor_init( - heartbeats_to_check, heartbeat_can_getters, heartbeat_can_updaters, &D_fakeCanHeartbeatSetter, - fault_can_setters, fault_can_getters); - - // check that there are no faults - E_fake_can_fault_state = false; - A_fake_can_fault_state = false; - - ASSERT_EQ(app_heartbeatMonitor_isSendingMissingHeartbeatFault(), false); - - // check that if there is a fault, it is detected - E_fake_can_fault_state = false; - A_fake_can_fault_state = true; - - ASSERT_EQ(app_heartbeatMonitor_isSendingMissingHeartbeatFault(), true); -} - -TEST(HeartbeatMonitor, test_create) -{ - /* test heartbeat monitor create */ - - // this hypothetical (fake) board only checks the A and E, and is the D - bool heartbeats_to_check[HEARTBEAT_BOARD_COUNT]; - for (bool &board : heartbeats_to_check) - { - board = false; - } - - heartbeats_to_check[A_HEARTBEAT_BOARD] = true; - heartbeats_to_check[E_HEARTBEAT_BOARD] = true; - - // create and assert time fields work - app_heartbeatMonitor_init( - heartbeats_to_check, heartbeat_can_getters, heartbeat_can_updaters, &D_fakeCanHeartbeatSetter, - fault_can_setters, fault_can_getters); - HeartbeatMonitor *heartbeat_monitor = app_heartbeatMonitor_get(); - - // check self heartbeat setter - ASSERT_EQ(heartbeat_monitor->self_checkin, &D_fakeCanHeartbeatSetter); - // assert status, checked in, to check, getters, updaters, and fault setters all set correctly - for (int board = 0; board < HEARTBEAT_BOARD_COUNT; board++) - { - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[board]); - ASSERT_FALSE(heartbeat_monitor->status[board]); - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[board], heartbeats_to_check[board]); - - ASSERT_EQ(heartbeat_monitor->getters[board], heartbeat_can_getters[board]); - ASSERT_EQ(heartbeat_monitor->resetters[board], heartbeat_can_updaters[board]); - ASSERT_EQ(heartbeat_monitor->fault_setters[board], fault_can_setters[board]); - } + ASSERT_FALSE(a_hbmonitor.status); + ASSERT_FALSE(a_hbmonitor.heartbeat_checked_in); + ASSERT_EQ(a_hbmonitor.timer.state, TIMER_STATE_EXPIRED); + ASSERT_TRUE(a_hbmonitor.fault_getter()); } -TEST(HeartbeatMonitor, test_broadcast_faults) +TEST_F(HeartbeatMonitorTest, test_broadcast_faults) { - /* test broadcast faults */ - // initialize monitor - - bool heartbeats_to_check[HEARTBEAT_BOARD_COUNT] = { [A_HEARTBEAT_BOARD] = true, - [B_HEARTBEAT_BOARD] = false, - [C_HEARTBEAT_BOARD] = false, - [D_HEARTBEAT_BOARD] = false, - [E_HEARTBEAT_BOARD] = true }; - - app_heartbeatMonitor_init( - heartbeats_to_check, heartbeat_can_getters, heartbeat_can_updaters, &D_fakeCanHeartbeatSetter, - fault_can_setters, fault_can_getters); - HeartbeatMonitor *heartbeat_monitor = app_heartbeatMonitor_get(); - // broadcast all good - for (bool &status : heartbeat_monitor->status) - { - status = true; - } - - app_heartbeatMonitor_broadcastFaults(); - - ASSERT_FALSE(E_fake_can_fault_state); - ASSERT_FALSE(A_fake_can_fault_state); - - // broadcast only A and E good (should be indistinguishable from all good) - for (bool &statu : heartbeat_monitor->status) - { - statu = false; - } - heartbeat_monitor->status[E_HEARTBEAT_BOARD] = true; - heartbeat_monitor->status[A_HEARTBEAT_BOARD] = true; - app_heartbeatMonitor_broadcastFaults(); - - ASSERT_FALSE(E_fake_can_fault_state); - ASSERT_FALSE(A_fake_can_fault_state); - - // broadcast only A good (should trigger E fault) - for (bool &status : heartbeat_monitor->status) - { - status = false; - } - heartbeat_monitor->status[E_HEARTBEAT_BOARD] = false; - heartbeat_monitor->status[A_HEARTBEAT_BOARD] = true; - app_heartbeatMonitor_broadcastFaults(); - - ASSERT_TRUE(E_fake_can_fault_state); - ASSERT_FALSE(A_fake_can_fault_state); - + a_hbmonitor.status = true; + app_heartbeatBoard_broadcastFaults(&a_hbmonitor); + ASSERT_FALSE(MOCK_BOARD_CAN_FAULT); // broadcast all bad - for (bool &status : heartbeat_monitor->status) - { - status = false; - } - app_heartbeatMonitor_broadcastFaults(); - - ASSERT_TRUE(E_fake_can_fault_state); - ASSERT_TRUE(A_fake_can_fault_state); + a_hbmonitor.status = false; + app_heartbeatBoard_broadcastFaults(&a_hbmonitor); + ASSERT_TRUE(MOCK_BOARD_CAN_FAULT); } -TEST(HeartbeatMonitor, test_check_in_and_tick) +TEST_F(HeartbeatMonitorTest, test_check_in_and_tick) { /* test tick and check in functions*/ fake_io_time_getCurrentMs_returns(0); - // our hypothetical D board relies on A and E - bool heartbeats_to_check[HEARTBEAT_BOARD_COUNT]; - for (bool &board : heartbeats_to_check) - { - board = false; - } - - heartbeats_to_check[A_HEARTBEAT_BOARD] = true; - heartbeats_to_check[E_HEARTBEAT_BOARD] = true; - - app_heartbeatMonitor_init( - heartbeats_to_check, heartbeat_can_getters, heartbeat_can_updaters, &D_fakeCanHeartbeatSetter, - fault_can_setters, fault_can_getters); - HeartbeatMonitor *heartbeat_monitor = app_heartbeatMonitor_get(); - // assert nothing changed (0 ms) - app_heartbeatMonitor_checkIn(); - - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[A_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[B_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[C_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[D_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[E_HEARTBEAT_BOARD]); - - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[A_HEARTBEAT_BOARD], heartbeats_to_check[A_HEARTBEAT_BOARD]); - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[B_HEARTBEAT_BOARD], heartbeats_to_check[B_HEARTBEAT_BOARD]); - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[C_HEARTBEAT_BOARD], heartbeats_to_check[C_HEARTBEAT_BOARD]); - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[D_HEARTBEAT_BOARD], heartbeats_to_check[D_HEARTBEAT_BOARD]); - ASSERT_EQ(heartbeat_monitor->is_watching_heartbeat_for[E_HEARTBEAT_BOARD], heartbeats_to_check[E_HEARTBEAT_BOARD]); - - // confirm status bad for non-null heartbeat boards - ASSERT_FALSE(heartbeat_monitor->status[A_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->status[E_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[B_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[C_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[D_HEARTBEAT_BOARD]); - - // check in A and E midway (150 ms) - fake_io_time_getCurrentMs_returns(150); - - E_fake_can_heartbeat_state = true; - A_fake_can_heartbeat_state = true; - D_fake_can_heartbeat_state = false; - - app_heartbeatMonitor_checkIn(); - - ASSERT_TRUE(D_fake_can_heartbeat_state); - ASSERT_FALSE(E_fake_can_heartbeat_state); - ASSERT_FALSE(A_fake_can_heartbeat_state); - - ASSERT_TRUE(heartbeat_monitor->heartbeats_checked_in[A_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->heartbeats_checked_in[E_HEARTBEAT_BOARD]); - - // progress to timeout period and verify all good (300 ms) - fake_io_time_getCurrentMs_returns(300); - app_heartbeatMonitor_checkIn(); - - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[A_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[B_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[C_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[D_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[E_HEARTBEAT_BOARD]); - - ASSERT_TRUE(heartbeat_monitor->status[A_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[B_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[C_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[D_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[E_HEARTBEAT_BOARD]); - - // check in E midway (450 ms) - fake_io_time_getCurrentMs_returns(450); - - E_fake_can_heartbeat_state = true; - A_fake_can_heartbeat_state = false; - D_fake_can_heartbeat_state = false; - - app_heartbeatMonitor_checkIn(); - - ASSERT_TRUE(D_fake_can_heartbeat_state); - ASSERT_FALSE(E_fake_can_heartbeat_state); - ASSERT_FALSE(A_fake_can_heartbeat_state); - - ASSERT_TRUE(heartbeat_monitor->heartbeats_checked_in[E_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[A_HEARTBEAT_BOARD]); - - // progress to timeout period and verify A missing (600 ms) - fake_io_time_getCurrentMs_returns(650); - app_heartbeatMonitor_checkIn(); - - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[A_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[B_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[C_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[D_HEARTBEAT_BOARD]); - ASSERT_FALSE(heartbeat_monitor->heartbeats_checked_in[E_HEARTBEAT_BOARD]); - - // A not checked in returns false - ASSERT_FALSE(heartbeat_monitor->status[A_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[B_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[C_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[D_HEARTBEAT_BOARD]); - ASSERT_TRUE(heartbeat_monitor->status[E_HEARTBEAT_BOARD]); + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_FALSE(a_hbmonitor.heartbeat_checked_in); + ASSERT_FALSE(a_hbmonitor.status); + + MOCK_CAN_HEARTBEAT_STATE = true; + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_TRUE(a_hbmonitor.heartbeat_checked_in); + ASSERT_TRUE(a_hbmonitor.status); + + // after a checkin, the CAN table should be reset to false + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_FALSE(a_hbmonitor.heartbeat_checked_in); + ASSERT_TRUE(a_hbmonitor.status); + + // right before the timer expires, the status should still be fine + fake_io_time_getCurrentMs_returns(timeout_ms - 1); + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_FALSE(a_hbmonitor.heartbeat_checked_in); + ASSERT_TRUE(a_hbmonitor.status); + + // only after the timeout, should the status be false + fake_io_time_getCurrentMs_returns(timeout_ms); + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_FALSE(a_hbmonitor.heartbeat_checked_in); + ASSERT_FALSE(a_hbmonitor.status); + + // with one checkin, the status should be fine again + fake_io_time_getCurrentMs_returns(timeout_ms + 1); + MOCK_CAN_HEARTBEAT_STATE = true; + app_heartbeatBoard_checkIn(&a_hbmonitor); + ASSERT_TRUE(a_hbmonitor.heartbeat_checked_in); + ASSERT_TRUE(a_hbmonitor.status); } \ No newline at end of file