Skip to content

Commit

Permalink
move implementation to interface and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanthecoder committed Jul 1, 2024
1 parent 052468e commit 1a67548
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 48 deletions.
96 changes: 92 additions & 4 deletions include/sensors/core/sensor_hardware_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ struct SensorHardwareConfiguration {
std::optional<gpio::PinConfig> 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<uint8_t>(mask_enum);
}
/** abstract sensor hardware device for a sync line */
class SensorHardwareBase {
public:
Expand All @@ -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<uint8_t>(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 {
Expand Down
45 changes: 1 addition & 44 deletions include/sensors/firmware/sensor_hardware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
12 changes: 12 additions & 0 deletions include/sensors/tests/mock_hardware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions sensors/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
166 changes: 166 additions & 0 deletions sensors/tests/test_sensor_hardware.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include <concepts>

#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);
}
}
}
}

0 comments on commit 1a67548

Please sign in to comment.