From 00a5b628e1455cfdec00283c5b716d6fc8b95683 Mon Sep 17 00:00:00 2001 From: Pascal Brunot Date: Sun, 2 Jun 2024 12:31:38 +0200 Subject: [PATCH] Bugfix #32 + Buzzer class and tests --- include/BoardLogic.hpp | 4 ++- include/Buzzer.hpp | 22 +++++++++++++++ include/PinsConfig.hpp | 3 +-- src/BoardLogic.cpp | 33 ++++++++++------------- src/Buzzer.cpp | 47 +++++++++++++++++++++++++++++++++ src/Machine.cpp | 8 +++--- test/test_logic/test_common.cpp | 8 ++++-- test/test_logic/test_logic.cpp | 40 ++++++++++++++++++++++++---- 8 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 include/Buzzer.hpp create mode 100644 src/Buzzer.cpp diff --git a/include/BoardLogic.hpp b/include/BoardLogic.hpp index 1c3320ae..61515aca 100644 --- a/include/BoardLogic.hpp +++ b/include/BoardLogic.hpp @@ -11,7 +11,7 @@ #include "card.hpp" #include "pins.hpp" #include "secrets.hpp" -#include +#include "Buzzer.hpp" namespace fabomatic { @@ -73,6 +73,7 @@ namespace fabomatic [[nodiscard]] auto getRebootRequest() const -> bool; [[nodiscard]] auto getServer() -> FabBackend &; [[nodiscard]] auto getMachineForTesting() -> Machine &; + [[nodiscard]] auto getBuzzerForTesting() -> Buzzer *; [[nodiscard]] auto getMachine() const -> const Machine &; [[nodiscard]] auto authorize(const card::uid_t uid) -> bool; [[nodiscard]] auto getHostname() const -> const std::string; @@ -102,6 +103,7 @@ namespace fabomatic BaseLCDWrapper &getLcd() const; bool rebootRequest{false}; + Buzzer buzzer; [[nodiscard]] auto longTap(const card::uid_t card, const std::string &short_prompt) const -> bool; }; diff --git a/include/Buzzer.hpp b/include/Buzzer.hpp new file mode 100644 index 00000000..86032aeb --- /dev/null +++ b/include/Buzzer.hpp @@ -0,0 +1,22 @@ +#ifndef BUZZER_HPP_ +#define BUZZER_HPP_ + +#include + +namespace fabomatic +{ + class Buzzer + { + private: + mutable uint16_t beepCount{0}; + + public: + auto configure() -> void; + auto beepOk() const -> void; + auto beepFail() const -> void; + // For testing purposes + auto getBeepCount() const -> uint16_t; + }; +} // namespace fabomatic + +#endif // BUZZER_HPP_ \ No newline at end of file diff --git a/include/PinsConfig.hpp b/include/PinsConfig.hpp index 1aabcc09..6b0c6079 100644 --- a/include/PinsConfig.hpp +++ b/include/PinsConfig.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace fabomatic { @@ -100,6 +99,6 @@ namespace fabomatic } return true; } - + } // namespace fabomatic #endif // PINSCONFIG_HPP_ \ No newline at end of file diff --git a/src/BoardLogic.cpp b/src/BoardLogic.cpp index 27f9f6d4..e32f503a 100644 --- a/src/BoardLogic.cpp +++ b/src/BoardLogic.cpp @@ -444,26 +444,12 @@ namespace fabomatic void BoardLogic::beepOk() const { - if constexpr (conf::buzzer::STANDARD_BEEP_DURATION > 0ms && pins.buzzer.pin != NO_PIN) - { - digitalWrite(pins.buzzer.pin, 1); - Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); - digitalWrite(pins.buzzer.pin, 0); - } + buzzer.beepOk(); } void BoardLogic::beepFail() const { - if constexpr (conf::buzzer::STANDARD_BEEP_DURATION > 0ms && pins.buzzer.pin != NO_PIN) - { - for (auto i = 0; i < conf::buzzer::NB_BEEPS; i++) - { - digitalWrite(pins.buzzer.pin, 1); - Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); - digitalWrite(pins.buzzer.pin, 0); - Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); - } - } + buzzer.beepFail(); } /// @brief Configures the board with the given references @@ -503,7 +489,7 @@ namespace fabomatic conf::machine::DEFAULT_GRACE_PERIOD); machine.configure(machine_conf, server); - + buzzer.configure(); auth.loadCache(); return success; @@ -598,11 +584,18 @@ namespace fabomatic /// @brief returns a modificable machine for testing only /// @return machine - Machine &BoardLogic::getMachineForTesting() + auto BoardLogic::getMachineForTesting() -> Machine & { return machine; } + /// @brief returns a modificable machine for testing only + /// @return a non-null Buzzer* + auto BoardLogic::getBuzzerForTesting() -> Buzzer * + { + return &buzzer; + } + /// @brief Gets the current machine /// @return a machine object auto BoardLogic::getMachine() const -> const Machine & @@ -661,6 +654,8 @@ namespace fabomatic auto BoardLogic::getHostname() const -> const std::string { // Hostname is BOARD + machine_id (which shall be unique) e.g. BOARD1 - return conf::default_config::hostname.data() + std::to_string(conf::default_config::machine_id.id); + return conf::default_config::hostname.data() + + std::to_string(conf::default_config::machine_id.id); } + } // namespace fabomatic \ No newline at end of file diff --git a/src/Buzzer.cpp b/src/Buzzer.cpp new file mode 100644 index 00000000..1db99b5f --- /dev/null +++ b/src/Buzzer.cpp @@ -0,0 +1,47 @@ +#include "Buzzer.hpp" +#include "conf.hpp" +#include "pins.hpp" +#include "Arduino.h" +#include "Tasks.hpp" + +namespace fabomatic +{ + auto Buzzer::configure() -> void + { + if constexpr (pins.buzzer.pin != NO_PIN) + { + pinMode(pins.buzzer.pin, OUTPUT); + } + } + auto Buzzer::beepOk() const -> void + { + if constexpr (conf::buzzer::STANDARD_BEEP_DURATION > 0ms && pins.buzzer.pin != NO_PIN) + { + digitalWrite(pins.buzzer.pin, 1); + Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); + digitalWrite(pins.buzzer.pin, 0); + } + beepCount++; + } + + auto Buzzer::beepFail() const -> void + { + if constexpr (conf::buzzer::STANDARD_BEEP_DURATION > 0ms && pins.buzzer.pin != NO_PIN) + { + for (auto i = 0; i < conf::buzzer::NB_BEEPS; i++) + { + digitalWrite(pins.buzzer.pin, 1); + Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); + digitalWrite(pins.buzzer.pin, 0); + Tasks::delay(conf::buzzer::STANDARD_BEEP_DURATION); + beepCount++; + } + } + } + + auto Buzzer::getBeepCount() const -> uint16_t + { + return beepCount; + } + +} // namespace fabomatic \ No newline at end of file diff --git a/src/Machine.cpp b/src/Machine.cpp index b50071f3..643482a9 100644 --- a/src/Machine.cpp +++ b/src/Machine.cpp @@ -106,7 +106,7 @@ namespace fabomatic } /// @brief indicates if the machine can be powered off - /// @return true if the delay has expired + /// @return true if the grace period has expired auto Machine::canPowerOff() const -> bool { if (!logoff_timestamp.has_value()) @@ -119,16 +119,16 @@ namespace fabomatic } /// @brief indicates if the machine is about to shudown and board should beep - /// @return true if shutdown is imminent + /// @return true if shutdown is imminent (within grace_period after last logoff) auto Machine::isShutdownImminent() const -> bool { - if (!logoff_timestamp.has_value() || conf::machine::BEEP_PERIOD == 0ms) + if (!logoff_timestamp.has_value()) return false; CHECK_CONFIGURED(bool); return (power_state == PowerState::WaitingPowerOff && - std::chrono::system_clock::now() - logoff_timestamp.value() > config.value().grace_period); + std::chrono::system_clock::now() - logoff_timestamp.value() <= config.value().grace_period); } /// @brief sets the machine power to on (true) or off (false) diff --git a/test/test_logic/test_common.cpp b/test/test_logic/test_common.cpp index c133683c..56758829 100644 --- a/test/test_logic/test_common.cpp +++ b/test/test_logic/test_common.cpp @@ -30,12 +30,16 @@ namespace fabomatic::tests { driver.setUid(uid.value(), duration_tap); TEST_ASSERT_TRUE_MESSAGE(uid == rfid.getUid(), "Card UID not equal"); - auto start = std::chrono::system_clock::now(); + auto start = millis(); do { logic.checkRfid(); delay(50); - } while (duration_tap.has_value() && std::chrono::system_clock::now() - start < duration_tap); + } while (duration_tap.has_value() && millis() - start < duration_tap.value_or(0ms).count()); + } + else if (duration_tap) + { + delay(duration_tap.value().count()); } return logic.getStatus(); } diff --git a/test/test_logic/test_logic.cpp b/test/test_logic/test_logic.cpp index d30cc394..7542e3bc 100644 --- a/test/test_logic/test_logic.cpp +++ b/test/test_logic/test_logic.cpp @@ -69,8 +69,19 @@ namespace fabomatic::tests void test_simple_methods() { - logic.beepFail(); - logic.beepOk(); + { + auto cpt = logic.getBuzzerForTesting()->getBeepCount(); + logic.beepFail(); + auto beeps = logic.getBuzzerForTesting()->getBeepCount() - cpt; + TEST_ASSERT_EQUAL_UINT16_MESSAGE(conf::buzzer::NB_BEEPS, beeps, "Buzzer FAIL has been beeped"); + } + { + auto cpt = logic.getBuzzerForTesting()->getBeepCount(); + logic.beepOk(); + auto beeps = logic.getBuzzerForTesting()->getBeepCount() - cpt; + TEST_ASSERT_EQUAL_UINT16_MESSAGE(1, beeps, "Buzzer OK has been beeped"); + } + logic.blinkLed(); std::vector statuses{BoardLogic::Status::Error, BoardLogic::Status::ErrorHardware, BoardLogic::Status::Connected, @@ -193,6 +204,8 @@ namespace fabomatic::tests void test_user_autologoff() { machine_init(logic, rfid); + auto &machine = logic.getMachineForTesting(); + machine.setGracePeriod(5s); TEST_ASSERT_TRUE_MESSAGE(logic.getMachine().getAutologoffDelay() == conf::machine::DEFAULT_AUTO_LOGOFF_DELAY, "Autologoff delay not default"); @@ -201,6 +214,7 @@ namespace fabomatic::tests simulate_rfid_card(rfid, logic, get_test_uid(0)); TEST_ASSERT_EQUAL_UINT16_MESSAGE(BoardLogic::Status::LoggedIn, logic.getStatus(), "Status not LoggedIn"); + TEST_ASSERT_TRUE_MESSAGE(machine.getPowerState() == Machine::PowerState::PoweredOn, "Machine is powered on"); // Card away simulate_rfid_card(rfid, logic, std::nullopt); @@ -209,14 +223,30 @@ namespace fabomatic::tests TEST_ASSERT_EQUAL_UINT16_MESSAGE(BoardLogic::Status::MachineInUse, logic.getStatus(), "Status not MachineInUse"); // Now shall expire afer 10s - simulate_rfid_card(rfid, logic, std::nullopt); - delay(10000); + simulate_rfid_card(rfid, logic, std::nullopt, 10s); TEST_ASSERT_TRUE_MESSAGE(logic.getMachine().isAutologoffExpired(), "Autologoff expired"); logic.logout(); - simulate_rfid_card(rfid, logic, std::nullopt); + simulate_rfid_card(rfid, logic, std::nullopt, 5s); TEST_ASSERT_TRUE_MESSAGE(logic.getMachine().isFree(), "Machine is free"); TEST_ASSERT_EQUAL_UINT16_MESSAGE(BoardLogic::Status::MachineFree, logic.getStatus(), "Status not MachineFree"); + TEST_ASSERT_TRUE_MESSAGE(machine.canPowerOff(), "Machine should be powered off"); + machine.power(false); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(Machine::PowerState::PoweredOff, machine.getPowerState(), "(2) Machine is powered off"); + + // Check Grace period + simulate_rfid_card(rfid, logic, get_test_uid(0), 1s); // Login + TEST_ASSERT_EQUAL_UINT8_MESSAGE(Machine::PowerState::PoweredOn, machine.getPowerState(), "(2) Machine is ON"); + simulate_rfid_card(rfid, logic, get_test_uid(0), 1s); // Logout + TEST_ASSERT_EQUAL_UINT8_MESSAGE(Machine::PowerState::WaitingPowerOff, machine.getPowerState(), "(2) Machine is waiting for power off"); + TEST_ASSERT_TRUE_MESSAGE(machine.isShutdownImminent(), "Machine in grace period"); + TEST_ASSERT_FALSE_MESSAGE(machine.canPowerOff(), "Machine cannot be powered off yet"); + simulate_rfid_card(rfid, logic, std::nullopt, 5s); // Let grace period expire + TEST_ASSERT_FALSE_MESSAGE(machine.isShutdownImminent(), "Machine is still in grace period"); + TEST_ASSERT_TRUE_MESSAGE(machine.canPowerOff(), "Machine can now be powered off"); + machine.power(false); + + TEST_ASSERT_EQUAL_UINT8_MESSAGE(Machine::PowerState::PoweredOff, machine.getPowerState(), "Machine is powered off"); auto save_result = logic.getServer().saveBuffer(); TEST_ASSERT_TRUE_MESSAGE(save_result, "Saving buffered messages works");