diff --git a/include/sensors/core/sensor_hardware_interface.hpp b/include/sensors/core/sensor_hardware_interface.hpp index 5a1a2f3af..0718889b8 100644 --- a/include/sensors/core/sensor_hardware_interface.hpp +++ b/include/sensors/core/sensor_hardware_interface.hpp @@ -16,6 +16,31 @@ struct SensorHardwareConfiguration { std::optional tip_sense = std::nullopt; }; +enum class SensorIdBitMask : uint8_t { + S0 = 0x01, + S1 = 0x02, + UNUSED = 0x00, + BOTH = 0x03, +}; + +static auto get_mask_from_id(can::ids::SensorId sensor) -> uint8_t { + auto mask_enum = SensorIdBitMask::UNUSED; + switch (sensor) { + case can::ids::SensorId::BOTH: + mask_enum = SensorIdBitMask::BOTH; + break; + case can::ids::SensorId::S0: + mask_enum = SensorIdBitMask::S0; + break; + case can::ids::SensorId::S1: + mask_enum = SensorIdBitMask::S1; + break; + case can::ids::SensorId::UNUSED: + default: + mask_enum = SensorIdBitMask::UNUSED; + } + return static_cast(mask_enum); +} /** abstract sensor hardware device for a sync line */ class SensorHardwareBase { public: @@ -29,10 +54,73 @@ class SensorHardwareBase { virtual auto set_sync() -> void = 0; virtual auto reset_sync() -> void = 0; virtual auto check_tip_presence() -> bool = 0; - virtual auto set_sync(can::ids::SensorId sensor) -> void = 0; - virtual auto reset_sync(can::ids::SensorId sensor) -> void = 0; - virtual auto set_sync_required(can::ids::SensorId sensor, bool required) -> void = 0; - virtual auto reset_sync_required(can::ids::SensorId sensor, bool required) -> void = 0; + + auto mask_satisfied() -> bool { + if (set_sync_required_mask != static_cast(SensorIdBitMask::UNUSED)) { + // if anything is "required" only sync when they are all triggered + return (sync_state_mask & set_sync_required_mask) == set_sync_required_mask; + } + return sync_state_mask & set_sync_enabled_mask; + } + + auto set_sync(can::ids::SensorId sensor) -> void { + printf("Set sync sensor called\n"); + // force the bit for this sensor to 1 + sync_state_mask |= get_mask_from_id(sensor); + if (mask_satisfied()) { + printf("satisfied calling sync\n"); + set_sync(); + } + } + + auto reset_sync(can::ids::SensorId sensor) -> void { + // force the bit for this sensor to 0 + printf("reset sync sensor called\n"); + sync_state_mask &= 0xFF ^ get_mask_from_id(sensor); + if (!mask_satisfied()) { + printf("No longer satisfied calling reset\n"); + reset_sync(); + } + } + + auto set_sync_enabled(can::ids::SensorId sensor, bool enabled) -> void { + printf("Set sync required called %d\n", enabled); + uint8_t applied_mask = get_mask_from_id(sensor); + if (!enabled) { + // force enabled bit to 0 + set_sync_enabled_mask &= 0xFF ^ applied_mask; + } else { + // force enabled bit to 1 + set_sync_enabled_mask |= applied_mask; + } + // update sync state now that requirements are different + if (mask_satisfied()) { + set_sync(); + } else { + reset_sync(); + } + } + + auto set_sync_required(can::ids::SensorId sensor, bool required) -> void { + uint8_t applied_mask = get_mask_from_id(sensor); + if (!required) { + // force required bit to 0 + set_sync_required_mask &= 0xFF ^ applied_mask; + } else { + // force required bit to 1 + set_sync_required_mask |= applied_mask; + } + // update sync state now that requirements are different + if (mask_satisfied()) { + set_sync(); + } else { + reset_sync(); + } + } + private: + uint8_t set_sync_required_mask = 0x00; + uint8_t set_sync_enabled_mask = 0x00; + uint8_t sync_state_mask = 0x00; }; struct SensorHardwareContainer { diff --git a/include/sensors/firmware/sensor_hardware.hpp b/include/sensors/firmware/sensor_hardware.hpp index 110d8a910..846f1351d 100644 --- a/include/sensors/firmware/sensor_hardware.hpp +++ b/include/sensors/firmware/sensor_hardware.hpp @@ -7,26 +7,6 @@ namespace sensors { namespace hardware { -enum class SensorIdBitMask : uint8_t { - S0 = 0x01, - S1 = 0x02, - UNUSED = 0x00, - BOTH = 0x03, -}; - -static auto get_mask_from_id(can::ids::SensorId sensor) -> SensorIdBitMask{ - switch(sensor) { - case can::ids::SensorId::BOTH: - return SensorIdBitMask::BOTH; - case can::ids::SensorId::S0 : - return SensorIdBitMask::S0; - case can::ids::SensorId::S1 : - return SensorIdBitMask::S1; - case can::ids::SensorId::UNUSED : - default : - return SensorIdBitMask::UNUSED; - } -} class SensorHardware : public SensorHardwareBase { public: @@ -40,31 +20,8 @@ class SensorHardware : public SensorHardwareBase { } return false; } - auto set_sync(can::ids::SensorId sensor) -> void { - sync_state_mask |= get_mask_from_id(sensor); - if (sync_state_mask & set_sync_required_mask == set_sync_required_mask) { - set_sync(); - } - } - - auto reset_sync(can::ids::SensorId sensor) -> void { - sync_state_mask &= 0xFF ^ get_mask_from_id(sensor); - if (sync_state_mask & set_sync_required_mask != set_sync_required_mask) { - reset_sync(); - } - } - - auto set_sync_required(can::ids::SensorId sensor, bool required) -> void { - uint8_t applied_mask = get_mask_from_id(sensor); - if (!required) { - applied_mask ^= 0xFF; - } - set_sync_required_mask &= applied_mask; - } - - uint8_t set_sync_required_mask = 0x00; - uint8_t sync_state_mask = 0x00; + sensors::hardware::SensorHardwareConfiguration hardware; }; }; // namespace hardware diff --git a/include/sensors/tests/mock_hardware.hpp b/include/sensors/tests/mock_hardware.hpp index fcfbac999..889de06a8 100644 --- a/include/sensors/tests/mock_hardware.hpp +++ b/include/sensors/tests/mock_hardware.hpp @@ -18,6 +18,18 @@ class MockSensorHardware : public sensors::hardware::SensorHardwareBase { auto get_sync_set_calls() const -> uint32_t { return sync_set_calls; } auto get_sync_reset_calls() const -> uint32_t { return sync_reset_calls; } + auto set_sync(can::ids::SensorId sensor) -> void { + sensors::hardware::SensorHardwareBase::set_sync(sensor); + } + auto reset_sync(can::ids::SensorId sensor) -> void { + sensors::hardware::SensorHardwareBase::reset_sync(sensor); + } + auto set_sync_enabled(can::ids::SensorId sensor, bool enabled) -> void { + sensors::hardware::SensorHardwareBase::set_sync_enabled(sensor, enabled); + } + auto set_sync_required(can::ids::SensorId sensor, bool required) -> void { + sensors::hardware::SensorHardwareBase::set_sync_required(sensor, required); + } private: bool sync_state = false; uint32_t sync_set_calls = 0; diff --git a/sensors/tests/CMakeLists.txt b/sensors/tests/CMakeLists.txt index b00f035c7..55f9d034b 100644 --- a/sensors/tests/CMakeLists.txt +++ b/sensors/tests/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable( test_pressure_sensor.cpp test_pressure_driver.cpp test_capacitive_sensor_utils.cpp + test_sensor_hardware.cpp ) target_include_directories(sensors PUBLIC ${CMAKE_SOURCE_DIR}/include) diff --git a/sensors/tests/test_sensor_hardware.cpp b/sensors/tests/test_sensor_hardware.cpp new file mode 100644 index 000000000..6f6d6b657 --- /dev/null +++ b/sensors/tests/test_sensor_hardware.cpp @@ -0,0 +1,166 @@ +#include + +#include "catch2/catch.hpp" +#include "can/core/messages.hpp" +#include "sensors/tests/mock_hardware.hpp" +#include "sensors/core/sensor_hardware_interface.hpp" + +constexpr auto sensor_id_primary = can::ids::SensorId::S0; +constexpr auto sensor_id_secondary = can::ids::SensorId::S1; + +SCENARIO("Multiple Sensors connected") { + test_mocks::MockSensorHardware mock_hw = test_mocks::MockSensorHardware{}; + GIVEN("One Sensor In use") { + WHEN("Sensor not enabled") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + WHEN("Sensor enabled") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync_enabled(sensor_id_primary, true); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + WHEN("Sensor required") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync_required(sensor_id_primary, true); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + WHEN("Sensor enabled and other sensor sets") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync_enabled(sensor_id_primary, true); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + WHEN("Resetting sync for in use sensor") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync_enabled(sensor_id_primary, true); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + mock_hw.reset_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + WHEN("Resetting sync for in other sensor") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync_enabled(sensor_id_primary, true); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + mock_hw.reset_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + } + GIVEN("Two sensors enabled") { + mock_hw.set_sync_enabled(sensor_id_primary, true); + mock_hw.set_sync_enabled(sensor_id_secondary, true); + WHEN("primary sets") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + WHEN("Secondary sets") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + WHEN("both are set") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_primary); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + WHEN("Sensors are disable") { + mock_hw.set_sync(sensor_id_primary); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + mock_hw.set_sync_enabled(sensor_id_primary, false); + REQUIRE(mock_hw.get_sync_state_mock() == true); + mock_hw.set_sync_enabled(sensor_id_secondary, false); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + + } + GIVEN("Two sensors required") { + mock_hw.set_sync_required(sensor_id_primary, true); + mock_hw.set_sync_required(sensor_id_secondary, true); + WHEN("primary sets") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + WHEN("Secondary sets") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + WHEN("both are set") { + REQUIRE(mock_hw.get_sync_state_mock() == false); + mock_hw.set_sync(sensor_id_primary); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + } +} + +SCENARIO("Controling multiple sensors at once") { + test_mocks::MockSensorHardware mock_hw{}; + GIVEN("Using the BOTH sensorid") { + WHEN("BOTH sensors are enabled") { + mock_hw.set_sync_enabled(can::ids::SensorId::BOTH, true); + THEN("Primary works") { + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + THEN("Secondary works") { + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + } + WHEN("BOTH sensors are required") { + mock_hw.set_sync_required(can::ids::SensorId::BOTH, true); + THEN("Primary wont solo trigger") { + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + THEN("Secondary wont solo trigger") { + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + THEN("both set triggers") { + mock_hw.set_sync(sensor_id_primary); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == true); + } + } + } + GIVEN("Using the NONE sensorid") { + WHEN("NONE sensors are enabled") { + mock_hw.set_sync_enabled(can::ids::SensorId::UNUSED, true); + THEN("Primary doesn't set") { + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + THEN("Secondary doesn't set") { + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + } + WHEN("NONE sensors are required") { + mock_hw.set_sync_required(can::ids::SensorId::UNUSED, true); + THEN("Primary wont solo trigger") { + mock_hw.set_sync(sensor_id_primary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + THEN("Secondary wont solo trigger") { + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + THEN("both set wont trigger") { + mock_hw.set_sync(sensor_id_primary); + mock_hw.set_sync(sensor_id_secondary); + REQUIRE(mock_hw.get_sync_state_mock() == false); + } + } + } +}