From 918f014f459c73ff6a8d4489d2831e74d4b33c31 Mon Sep 17 00:00:00 2001 From: RobertGawron Date: Tue, 17 Dec 2024 11:43:33 +0100 Subject: [PATCH] add dev and ut for measurement data handling --- DevOps/README.md | 9 +- Documentation/Diagrams/TODO | 24 ++++ .../Driver/Inc/PulseCounterDriverStub.hpp | 12 ++ .../Driver/Src/PulseCounterDriverStub.cpp | 33 +++++ .../Application/Driver/Src/UartDriverStub.cpp | 11 +- .../Inc/MeasurementCoordinator.hpp | 26 ++-- .../Inc/MeasurementDataStore.hpp | 17 +-- .../Interfaces/IMeasurementDataStore.hpp | 104 +++++++++++++++ .../BusinessLogic/Src/ApplicationBuilder.cpp | 10 +- .../Src/MeasurementCoordinator.cpp | 25 +++- .../Src/MeasurementDataStore.cpp | 24 +++- .../Device/Inc/MeasurementType.hpp | 18 +++ .../Inc/PulseCounterMeasurementSource.hpp | 20 +-- .../Device/Inc/SdCardMeasurementRecorder.hpp | 13 +- .../Device/Inc/UartMeasurementRecorder.hpp | 13 +- .../Device/Inc/UartMeasurementSource.hpp | 20 +-- .../Device/Inc/WiFiMeasurementRecorder.hpp | 16 +-- .../Interfaces/IMeasurementRecorder.hpp | 14 +-- .../Device/Interfaces/IMeasurementSource.hpp | 25 +--- .../Src/PulseCounterMeasurementSource.cpp | 24 +++- .../Device/Src/SdCardMeasurementRecorder.cpp | 10 +- .../Device/Src/UartMeasurementRecorder.cpp | 10 +- .../Device/Src/UartMeasurementSource.cpp | 12 +- .../Device/Src/WiFiMeasurementRecorder.cpp | 88 ++++++++++--- .../Driver/Inc/PulseCounterDriver.hpp | 23 ++-- .../Driver/Interfaces/IPulseCounterDriver.hpp | 9 +- .../Driver/Src/PulseCounterDriver.cpp | 42 +++++++ Test/System/test_pulse_counter.py | 24 ++++ .../test_MeasurementCoordinator.cpp | 119 ++++++++++++++++++ .../test_MeasurementDataStore.cpp | 105 ++++++++++++++++ Test/Unit/CMakeLists.txt | 44 +++++++ .../test_PulseCounterMeasurementSource.cpp | 79 ++++++++++++ .../Device/test_WiFiMeasurementRecorder.cpp | 114 +++++++++++++++++ Test/Unit/Driver/test_PulseCounterDriver.cpp | 57 +++++++++ 34 files changed, 1015 insertions(+), 179 deletions(-) create mode 100644 Documentation/Diagrams/TODO create mode 100644 Software/STM32F103RBTx/Application/BusinessLogic/Interfaces/IMeasurementDataStore.hpp create mode 100644 Software/STM32F103RBTx/Application/Device/Inc/MeasurementType.hpp create mode 100644 Test/System/test_pulse_counter.py create mode 100644 Test/Unit/BusinessLogic/test_MeasurementCoordinator.cpp create mode 100644 Test/Unit/BusinessLogic/test_MeasurementDataStore.cpp create mode 100644 Test/Unit/Device/test_PulseCounterMeasurementSource.cpp create mode 100644 Test/Unit/Device/test_WiFiMeasurementRecorder.cpp create mode 100644 Test/Unit/Driver/test_PulseCounterDriver.cpp diff --git a/DevOps/README.md b/DevOps/README.md index ec3a7f31..6ee53f93 100644 --- a/DevOps/README.md +++ b/DevOps/README.md @@ -100,13 +100,18 @@ Note: If using Windows, [MobaXterm](https://mobaxterm.mobatek.net/download-home- cd /workspace/build/ && cmake .. && make -j24 -cd /workspace/Test/SystemTests +cd /workspace/Test/System python3 -m venv /workspace/venv pytest test_display.py -s --html=report.html after: -cd /workspace/build/ && cmake .. && make -j24 && cd /workspace/Test/SystemTests && pytest test_display.py -s --html=report.html +cd /workspace/build/ && cmake .. && make -j24 && cd /workspace/Test/System && pytest test_display.py -s --html=report.html + + + +cd /workspace/build/ && cmake .. && make -j24 && cd /workspace/Test/System && pytest test_pulse_counter.py -s --html=report.html + diff --git a/Documentation/Diagrams/TODO b/Documentation/Diagrams/TODO new file mode 100644 index 00000000..9323c3d9 --- /dev/null +++ b/Documentation/Diagrams/TODO @@ -0,0 +1,24 @@ +@startuml +state "Idle" as IDLE +state "Initialize" as INIT +state "Run" as RUN +state "Sleep" as SLEEP +state "Fault" as FAULT +state "Power Off" as POWER_OFF + +[*] --> IDLE : Power On +IDLE --> INIT : onInitialize() +INIT --> RUN : Initialization Complete + +RUN --> SLEEP : onStop() +SLEEP --> RUN : onStart() + +RUN --> FAULT : Fault Detected +FAULT --> INIT : Recover and Reinitialize + +RUN --> POWER_OFF : onPowerOff() +SLEEP --> POWER_OFF : onPowerOff() +FAULT --> POWER_OFF : Emergency Shutdown + +POWER_OFF --> [*] : Power Down Complete +@enduml \ No newline at end of file diff --git a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Inc/PulseCounterDriverStub.hpp b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Inc/PulseCounterDriverStub.hpp index f7cd7771..a3431eaf 100644 --- a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Inc/PulseCounterDriverStub.hpp +++ b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Inc/PulseCounterDriverStub.hpp @@ -4,11 +4,20 @@ #include "Driver/Interfaces/IPulseCounterDriver.hpp" +extern "C" +{ + void incrementPulseCounter(std::uint8_t counterId); +} + namespace Driver { class PulseCounterDriverStub : public IPulseCounterDriver { public: + static const std::uint8_t PULSE_COUNTER_AMOUNT = 4u; + + explicit PulseCounterDriverStub(PulseCounterIdentifier id); + explicit PulseCounterDriverStub(); virtual ~PulseCounterDriverStub() = default; @@ -17,6 +26,9 @@ namespace Driver PulseCounterDriverStub(const PulseCounterDriverStub &) = delete; PulseCounterDriverStub &operator=(const PulseCounterDriverStub &) = delete; + IPulseCounterDriver::CounterSizeType getMeasurement() override; + void clearMeasurement() override; + bool onInitialize() override; bool onStart() override; diff --git a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/PulseCounterDriverStub.cpp b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/PulseCounterDriverStub.cpp index ad41c363..81aba8d2 100644 --- a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/PulseCounterDriverStub.cpp +++ b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/PulseCounterDriverStub.cpp @@ -1,5 +1,27 @@ #include "PulseCounterDriverStub.hpp" +#include +#include + +#include + +// Declare a std::array to store pulse counts +static std::array pulseCounters = {0}; + +// Expose the array pointer for C compatibility +extern "C" +{ + + // C-compatible function to increment a specific counter + void incrementPulseCounter(std::uint8_t counterId) + { + if (counterId < Driver::PulseCounterDriverStub::PULSE_COUNTER_AMOUNT) + { + pulseCounters[counterId]++; + } + } +} + namespace Driver { PulseCounterDriverStub::PulseCounterDriverStub() @@ -25,4 +47,15 @@ namespace Driver { return true; } + + IPulseCounterDriver::CounterSizeType PulseCounterDriverStub::getMeasurement() + { + // printf("PulseCounterDriverStub::getMeasurement()\n"); + return 5; + } + + void PulseCounterDriverStub::clearMeasurement() + { + } + } diff --git a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/UartDriverStub.cpp b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/UartDriverStub.cpp index 2889bc08..67ce03cd 100644 --- a/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/UartDriverStub.cpp +++ b/Simulation/FirmwarePCSimulator/Mock/Application/Driver/Src/UartDriverStub.cpp @@ -1,4 +1,6 @@ #include "UartDriverStub.hpp" +#include +#include namespace Driver { @@ -25,9 +27,12 @@ namespace Driver UartExchangeStatus UartDriverStub::transmit(std::uint8_t *data, std::uint16_t size, std::uint32_t timeout) { - (void)data; // Mark data as unused - (void)size; // Mark size as unused - (void)timeout; // Mark timeout as unused + + for (std::size_t i = 0u; i < size; i++) + { + printf("TX %d ", data[i]); + } + printf("\n"); return UartExchangeStatus::Ok; } diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementCoordinator.hpp b/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementCoordinator.hpp index 4f6c3d7f..a61738a9 100644 --- a/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementCoordinator.hpp +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementCoordinator.hpp @@ -7,10 +7,10 @@ #ifndef MeasurementCoordinator_h #define MeasurementCoordinator_h -#include -#include "Device/Interfaces/IMeasurementSource.hpp" -#include "BusinessLogic/Inc/MeasurementDataStore.hpp" +#include "BusinessLogic/Interfaces/IMeasurementDataStore.hpp" #include "BusinessLogic/Inc/SaferArray.hpp" +#include "Device/Interfaces/IMeasurementSource.hpp" +#include namespace BusinessLogic { @@ -32,7 +32,7 @@ namespace BusinessLogic * * @param storage Reference to a MeasurementDataStore object that handles the storage of measurement data. */ - explicit MeasurementCoordinator(MeasurementDataStore &storage); + explicit MeasurementCoordinator(IMeasurementDataStore &storage); /** * @brief Deleted default constructor to prevent instantiation without a storage reference. @@ -75,14 +75,6 @@ namespace BusinessLogic */ virtual bool tick(); - /** - * @brief Updates measurements from all registered input devices. - * - * This function queries all registered measurement sources for new data, processes it, - * and ensures the data is ready to be stored. - */ - void updateMeasurements(); - /** * @brief Registers an input device observer to be periodically queried for measurement data. * @@ -107,6 +99,14 @@ namespace BusinessLogic bool removeObserver(Device::IMeasurementSource &observer); private: + /** + * @brief Updates measurements from all registered input devices. + * + * This function queries all registered measurement sources for new data, processes it, + * and ensures the data is ready to be stored. + */ + void updateMeasurements(); + /** @brief Maximum number of observers that can be registered. */ static const std::uint8_t MaxObservers{5u}; @@ -118,7 +118,7 @@ namespace BusinessLogic * * This member is responsible for notifying the storage objects when new measurement data is ready. */ - MeasurementDataStore &storage; + IMeasurementDataStore &storage; }; } diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementDataStore.hpp b/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementDataStore.hpp index 301e92ab..fe6d20a7 100644 --- a/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementDataStore.hpp +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Inc/MeasurementDataStore.hpp @@ -7,8 +7,9 @@ #ifndef MeasurementDataStore_H_ #define MeasurementDataStore_H_ -#include "Device/Interfaces/IMeasurementRecorder.hpp" +#include "BusinessLogic/Interfaces/IMeasurementDataStore.hpp" #include "BusinessLogic/Inc/SaferArray.hpp" +#include "Device/Interfaces/IMeasurementRecorder.hpp" #include namespace BusinessLogic @@ -22,7 +23,7 @@ namespace BusinessLogic * It is responsible for initializing and notifying all registered observers when new * measurement data is ready. */ - class MeasurementDataStore + class MeasurementDataStore : public IMeasurementDataStore { public: /** @@ -33,7 +34,7 @@ namespace BusinessLogic /** * @brief Destructor for MeasurementDataStore. */ - ~MeasurementDataStore() = default; + ~MeasurementDataStore() override = default; /** * @brief Deleted copy constructor to prevent copying. @@ -55,7 +56,7 @@ namespace BusinessLogic * @param observer Reference to an object that implements IMeasurementRecorder. * @return True if the observer was successfully added; false otherwise. */ - bool addObserver(Device::IMeasurementRecorder &observer); + bool addObserver(Device::IMeasurementRecorder &observer) override; /** * @brief Removes an observer that records measurement data. @@ -67,7 +68,7 @@ namespace BusinessLogic * @return True if the observer was successfully removed; false otherwise. * @note Removing the observer does not automatically deinitialize it. */ - bool removeObserver(Device::IMeasurementRecorder &observer); + bool removeObserver(Device::IMeasurementRecorder &observer) override; /** * @brief Initializes the MeasurementDataStore and all registered observers. @@ -78,7 +79,7 @@ namespace BusinessLogic * @return True if initialization was successful; false otherwise. * @note Observers should not be initialized manually in other classes such as ApplicationBuilder. */ - bool initialize(); + bool initialize() override; /** * @brief Starts the MeasurementDataStore and all registered observers. @@ -88,7 +89,7 @@ namespace BusinessLogic * * @return True if start was successful; false otherwise. */ - bool start(); + bool start() override; /** * @brief Notifies all registered observers that new measurement data is ready to be stored. @@ -96,7 +97,7 @@ namespace BusinessLogic * This function iterates through the list of registered observers and notifies each one * that new measurement data is available for storage. */ - void notifyObservers(); + bool notifyObservers(Device::MeasurementType measurement) override; private: /** @brief Maximum number of observers that can be registered. */ diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Interfaces/IMeasurementDataStore.hpp b/Software/STM32F103RBTx/Application/BusinessLogic/Interfaces/IMeasurementDataStore.hpp new file mode 100644 index 00000000..8f396e6f --- /dev/null +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Interfaces/IMeasurementDataStore.hpp @@ -0,0 +1,104 @@ +/** + * @file IMeasurementDataStore.hpp + * @brief Defines the IMeasurementDataStore class which manages the storage of measurement data + * using the observer pattern. + */ + +#ifndef IMeasurementDataStore_H_ +#define IMeasurementDataStore_H_ + +#include "BusinessLogic/Inc/SaferArray.hpp" +#include "Device/Interfaces/IMeasurementRecorder.hpp" +#include "Device/Interfaces/IMeasurementSource.hpp" +#include + +namespace BusinessLogic +{ + /** + * @class IMeasurementDataStore + * @brief Implements the observer pattern to manage a collection of measurement recorders. + * + * This class allows clients to add or remove observers that store measurement data. + * Observers can represent different storage methods such as SD card, Wi-Fi, UART, etc. + * It is responsible for initializing and notifying all registered observers when new + * measurement data is ready. + */ + class IMeasurementDataStore + { + public: + /** + * @brief Default constructor for IMeasurementDataStore. + */ + explicit IMeasurementDataStore() = default; + + /** + * @brief Destructor for IMeasurementDataStore. + */ + virtual ~IMeasurementDataStore() = default; + + /** + * @brief Deleted copy constructor to prevent copying. + */ + IMeasurementDataStore(const IMeasurementDataStore &) = delete; + + /** + * @brief Deleted assignment operator to prevent assignment. + * @return IMeasurementDataStore& The assigned object. + */ + IMeasurementDataStore &operator=(const IMeasurementDataStore &) = delete; + + /** + * @brief Adds an observer that records measurement data. + * + * The observer should implement the IMeasurementRecorder interface, and this method will + * add it to the list of observers managed by the IMeasurementDataStore. + * + * @param observer Reference to an object that implements IMeasurementRecorder. + * @return True if the observer was successfully added; false otherwise. + */ + virtual bool addObserver(Device::IMeasurementRecorder &observer) = 0; + + /** + * @brief Removes an observer that records measurement data. + * + * This method removes an observer from the list of registered observers, stopping it + * from receiving updates about new measurement data. + * + * @param observer Reference to an object that implements IMeasurementRecorder. + * @return True if the observer was successfully removed; false otherwise. + * @note Removing the observer does not automatically deinitialize it. + */ + virtual bool removeObserver(Device::IMeasurementRecorder &observer) = 0; + + /** + * @brief Initializes the IMeasurementDataStore and all registered observers. + * + * This function initializes the IMeasurementDataStore and all the objects + * registered via addObserver(). + * + * @return True if initialization was successful; false otherwise. + * @note Observers should not be initialized manually in other classes such as ApplicationBuilder. + */ + virtual bool initialize() = 0; + + /** + * @brief Starts the IMeasurementDataStore and all registered observers. + * + * This function starts the IMeasurementDataStore and prepares all registered observers to start + * recording measurement data. + * + * @return True if start was successful; false otherwise. + */ + virtual bool start() = 0; + + /** + * @brief Notifies all registered observers that new measurement data is ready to be stored. + * + * This function iterates through the list of registered observers and notifies each one + * that new measurement data is available for storage. + */ + virtual bool notifyObservers(Device::MeasurementType measurement) = 0; + }; +} + +#endif // IMeasurementDataStore_H_ diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Src/ApplicationBuilder.cpp b/Software/STM32F103RBTx/Application/BusinessLogic/Src/ApplicationBuilder.cpp index 939a9a18..fcc47de7 100644 --- a/Software/STM32F103RBTx/Application/BusinessLogic/Src/ApplicationBuilder.cpp +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Src/ApplicationBuilder.cpp @@ -49,8 +49,8 @@ namespace BusinessLogic bool status = false; // clang-format off - if (dataStore.start() - && hmiFactory.start()) + if (/*measurementCoordinator.start() + &&*/ hmiFactory.start()) // clang-format on { status = true; @@ -69,11 +69,9 @@ namespace BusinessLogic // can we recover from fault at any place here? I dont know. bool status = false; - dataStore.notifyObservers(); // clang-format off - if (/*measurementCoordinator.tick()*/ - - hmiFactory.tick()) + if (measurementCoordinator.tick() + && hmiFactory.tick()) // clang-format on { status = true; diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementCoordinator.cpp b/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementCoordinator.cpp index 4667515d..5cc54f95 100644 --- a/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementCoordinator.cpp +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementCoordinator.cpp @@ -2,11 +2,12 @@ #include "BusinessLogic/Inc/MeasurementDataStore.hpp" #include +#include namespace BusinessLogic { - MeasurementCoordinator::MeasurementCoordinator(MeasurementDataStore &_storage) : storage(_storage) + MeasurementCoordinator::MeasurementCoordinator(IMeasurementDataStore &_storage) : storage(_storage) { } @@ -15,9 +16,21 @@ namespace BusinessLogic bool status = true; // abort on first fail - for (std::size_t i = 0u; (i < observers.size()) && status; i++) + for (std::size_t i = 0u; i < observers.size(); i++) { - status &= observers[i]->init(); + status = observers[i]->initialize(); + + if (!status) + { + break; + } + + status = observers[i]->start(); + + if (!status) + { + break; + } } return status; @@ -39,7 +52,11 @@ namespace BusinessLogic if (isMeasurementReady) { - observers[i]->getMeasurement(); + + Device::MeasurementType measurement = observers[i]->getMeasurement(); + storage.notifyObservers(measurement); + + // printf("updateMeasurements() %d\n", measurement); } } } diff --git a/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementDataStore.cpp b/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementDataStore.cpp index 18143d1d..cd1a068e 100644 --- a/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementDataStore.cpp +++ b/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementDataStore.cpp @@ -9,9 +9,14 @@ namespace BusinessLogic bool status = true; // abort on first fail - for (std::size_t i = 0u; (i < observers.size()) && status; i++) + for (std::size_t i = 0u; i < observers.size(); i++) { - status &= observers[i]->initialize(); + status = observers[i]->initialize(); + + if (!status) + { + break; + } } return status; @@ -22,9 +27,14 @@ namespace BusinessLogic bool status = true; // abort on first fail - for (std::size_t i = 0u; (i < observers.size()) && status; i++) + for (std::size_t i = 0u; i < observers.size(); i++) { - status &= observers[i]->start(); + status = observers[i]->start(); + + if (!status) + { + break; + } } return status; @@ -42,11 +52,13 @@ namespace BusinessLogic return status; } - void MeasurementDataStore::notifyObservers() + bool MeasurementDataStore::notifyObservers(Device::MeasurementType measurement) { for (std::size_t i = 0u; i < observers.size(); i++) { - observers[i]->notify(); + observers[i]->notify(measurement); } + + return true; } } diff --git a/Software/STM32F103RBTx/Application/Device/Inc/MeasurementType.hpp b/Software/STM32F103RBTx/Application/Device/Inc/MeasurementType.hpp new file mode 100644 index 00000000..7551cfb9 --- /dev/null +++ b/Software/STM32F103RBTx/Application/Device/Inc/MeasurementType.hpp @@ -0,0 +1,18 @@ + + +#ifndef MeasurementType_h +#define MeasurementType_h + +#include +#include + +#include + +namespace Device +{ + // Define a type alias for measurement types + using MeasurementType = std::variant; + +} + +#endif diff --git a/Software/STM32F103RBTx/Application/Device/Inc/PulseCounterMeasurementSource.hpp b/Software/STM32F103RBTx/Application/Device/Inc/PulseCounterMeasurementSource.hpp index 8ab96323..4a1151e8 100644 --- a/Software/STM32F103RBTx/Application/Device/Inc/PulseCounterMeasurementSource.hpp +++ b/Software/STM32F103RBTx/Application/Device/Inc/PulseCounterMeasurementSource.hpp @@ -23,6 +23,8 @@ namespace Device class PulseCounterMeasurementSource : public IMeasurementSource { public: + using MeasurementSizeType = Driver::IPulseCounterDriver::CounterSizeType; + /** * @brief Constructs a PulseCounterMeasurementSource with a reference to a PulseCounterDriver. * @@ -51,19 +53,9 @@ namespace Device */ PulseCounterMeasurementSource &operator=(const PulseCounterMeasurementSource &) = delete; - /** - * @brief Initializes the pulse counter, setting the initial pulse count to zero. - * - * @return True if initialization was successful, false otherwise. - */ - bool init() override; - - /** - * @brief Deinitializes the pulse counter device. - * - * @return True if deinitialization was successful, false otherwise. - */ - bool deinit() override; + bool initialize() override; + bool start() override; + bool stop() override; /** * @brief Checks if a new measurement is available from the pulse counter. @@ -75,7 +67,7 @@ namespace Device /** * @brief Retrieves the current pulse count from the pulse counter device. */ - void getMeasurement() override; + MeasurementType getMeasurement() override; private: /** @brief Reference to the driver responsible for interacting with the pulse counter device. */ diff --git a/Software/STM32F103RBTx/Application/Device/Inc/SdCardMeasurementRecorder.hpp b/Software/STM32F103RBTx/Application/Device/Inc/SdCardMeasurementRecorder.hpp index 57bcb224..10d4061d 100644 --- a/Software/STM32F103RBTx/Application/Device/Inc/SdCardMeasurementRecorder.hpp +++ b/Software/STM32F103RBTx/Application/Device/Inc/SdCardMeasurementRecorder.hpp @@ -49,19 +49,12 @@ namespace Device */ SdCardMeasurementRecorder &operator=(const SdCardMeasurementRecorder &) = delete; - /** - * @brief Writes measurement data to the SD card. - * - * This method writes the prepared measurement data to the SD card using the SD card driver. - */ - void write() override; - - /** + /** * @brief Flushes any remaining data to the SD card. * * This method ensures that any remaining buffered data is written to the SD card. */ - void flush() override; + bool flush() override; /** * @brief Notifies the recorder to process new data. @@ -69,7 +62,7 @@ namespace Device * This method is called to notify the recorder that new measurement data is available * and should be written to the SD card. */ - void notify() override; + bool notify(Device::MeasurementType &measurement) override; protected: /** diff --git a/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementRecorder.hpp b/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementRecorder.hpp index dbe23b01..7929ec1d 100644 --- a/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementRecorder.hpp +++ b/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementRecorder.hpp @@ -46,19 +46,12 @@ namespace Device */ UartMeasurementRecorder &operator=(const UartMeasurementRecorder &) = delete; - /** - * @brief Writes measurement data to the COM port via UART. - * - * This method writes the prepared measurement data to the COM port using the UART driver. - */ - void write() override; - - /** + /** * @brief Flushes any remaining data to the COM port via UART. * * This method ensures that any remaining buffered data is sent to the COM port via UART. */ - void flush() override; + bool flush() override; /** * @brief Notifies the recorder to process new data. @@ -66,7 +59,7 @@ namespace Device * This method is called to notify the recorder that new measurement data is available * and should be sent to the COM port via UART. */ - void notify() override; + bool notify(Device::MeasurementType &measurement) override; protected: /** diff --git a/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementSource.hpp b/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementSource.hpp index 428487cb..0632982a 100644 --- a/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementSource.hpp +++ b/Software/STM32F103RBTx/Application/Device/Inc/UartMeasurementSource.hpp @@ -50,21 +50,9 @@ namespace Device */ UartMeasurementSource &operator=(const UartMeasurementSource &) = delete; - /** - * @brief Initializes the UART measurement source. - * - * Prepares the UART device for receiving measurement data. - * @return True if initialization was successful, false otherwise. - */ - bool init() override; - - /** - * @brief Deinitializes the UART measurement source. - * - * Shuts down the communication with the UART device. - * @return True if deinitialization was successful, false otherwise. - */ - bool deinit() override; + bool initialize() override; + bool start() override; + bool stop() override; /** * @brief Checks if new measurement data is available from the UART device. @@ -78,7 +66,7 @@ namespace Device * * This method fetches the latest measurement data received via UART. */ - void getMeasurement() override; + MeasurementType getMeasurement() override; private: /** @brief Reference to the UART driver used for communication with the UART device. */ diff --git a/Software/STM32F103RBTx/Application/Device/Inc/WiFiMeasurementRecorder.hpp b/Software/STM32F103RBTx/Application/Device/Inc/WiFiMeasurementRecorder.hpp index fbe82e4a..8f1422c2 100644 --- a/Software/STM32F103RBTx/Application/Device/Inc/WiFiMeasurementRecorder.hpp +++ b/Software/STM32F103RBTx/Application/Device/Inc/WiFiMeasurementRecorder.hpp @@ -57,7 +57,7 @@ namespace Device * This method is called to notify the recorder that new measurement data is available and * should be sent to the ESP module via UART. */ - void notify() override; + bool notify(Device::MeasurementType &measurement) override; protected: /** @@ -93,20 +93,20 @@ namespace Device bool onReset() override; /** - * @brief Writes the measurement data to the ESP module via UART. + * @brief Flushes any remaining data to the ESP module. * - * This method sends the prepared measurement data to the ESP module for transmission over WiFi. + * This method ensures that any remaining measurement data is sent to the ESP module via UART. */ - void write() override; + bool flush() override; + private: /** - * @brief Flushes any remaining data to the ESP module. + * @brief Writes the measurement data to the ESP module via UART. * - * This method ensures that any remaining measurement data is sent to the ESP module via UART. + * This method sends the prepared measurement data to the ESP module for transmission over WiFi. */ - void flush() override; + virtual bool write(Device::MeasurementType &measurement); - private: /** @brief Reference to the UART driver used for communication with the ESP module. */ Driver::IUartDriver &driver; }; diff --git a/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementRecorder.hpp b/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementRecorder.hpp index b961b5a1..e5625cd2 100644 --- a/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementRecorder.hpp +++ b/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementRecorder.hpp @@ -1,7 +1,9 @@ #ifndef MeasurementRecorder_h #define MeasurementRecorder_h +#include "Device/Inc/MeasurementType.hpp" #include "Driver/Inc/DriverState.hpp" + /** * @file MeasurementRecorder.h * @brief Declaration of the IMeasurementRecorder interface class for storing measurement data. @@ -35,28 +37,20 @@ namespace Device */ ~IMeasurementRecorder() override = default; - /** - * @brief Writes measurement data. - * - * This method should be implemented to write measurement data to the chosen storage medium. - * The data might be buffered before being written, depending on the implementation. - */ - virtual void write() = 0; - /** * @brief Flushes the data to the storage medium. * * This method should be implemented to flush any buffered data to the storage medium, ensuring * that all data is physically written and not just stored in a temporary buffer. */ - virtual void flush() = 0; + virtual bool flush() = 0; /** * @brief Notifies about new data to be saved. * * This method should be implemented to handle any necessary actions when new data is ready to be saved. */ - virtual void notify() = 0; + virtual bool notify(Device::MeasurementType &measurement) = 0; }; } diff --git a/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementSource.hpp b/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementSource.hpp index fcbf3f25..04c654d7 100644 --- a/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementSource.hpp +++ b/Software/STM32F103RBTx/Application/Device/Interfaces/IMeasurementSource.hpp @@ -10,6 +10,8 @@ * pulse counters or UART devices. */ +#include "Device/Inc/MeasurementType.hpp" + namespace Device { /** @@ -33,24 +35,9 @@ namespace Device */ virtual ~IMeasurementSource() = default; - /** - * @brief Initializes communication with the measurement device. - * - * This method should be implemented to set up necessary configurations and establish communication - * with the measurement device. - * - * @return true if initialization is successful, false otherwise. - */ - virtual bool init() = 0; - - /** - * @brief Stops communication with the measurement device. - * - * This method should be implemented to properly shut down or disconnect from the measurement device. - * - * @return true if deinitialization is successful, false otherwise. - */ - virtual bool deinit() = 0; + virtual bool initialize() = 0; + virtual bool start() = 0; + virtual bool stop() = 0; /** * @brief Checks if a measurement is available. @@ -69,7 +56,7 @@ namespace Device * This method should be implemented to obtain the current measurement data from the device. * It is not fully implemented yet. */ - virtual void getMeasurement() = 0; + virtual MeasurementType getMeasurement() = 0; }; } diff --git a/Software/STM32F103RBTx/Application/Device/Src/PulseCounterMeasurementSource.cpp b/Software/STM32F103RBTx/Application/Device/Src/PulseCounterMeasurementSource.cpp index 5730aa1a..283bc777 100644 --- a/Software/STM32F103RBTx/Application/Device/Src/PulseCounterMeasurementSource.cpp +++ b/Software/STM32F103RBTx/Application/Device/Src/PulseCounterMeasurementSource.cpp @@ -1,6 +1,8 @@ #include "Device/Inc/PulseCounterMeasurementSource.hpp" #include "Driver/Interfaces/IPulseCounterDriver.hpp" +#include +#include namespace Device { @@ -9,15 +11,22 @@ namespace Device { } - bool PulseCounterMeasurementSource::init() + bool PulseCounterMeasurementSource::initialize() { - pulseCounterDriver.initialize(); - return true; + const bool status = pulseCounterDriver.initialize(); + return status; } - bool PulseCounterMeasurementSource::deinit() + bool PulseCounterMeasurementSource::start() { - return true; + const bool status = pulseCounterDriver.start(); + return status; + } + + bool PulseCounterMeasurementSource::stop() + { + const bool status = pulseCounterDriver.stop(); + return status; } bool PulseCounterMeasurementSource::isMeasurementAvailable() @@ -25,8 +34,11 @@ namespace Device return true; } - void PulseCounterMeasurementSource::getMeasurement() + MeasurementType PulseCounterMeasurementSource::getMeasurement() { + + // printf("PulseCounterMeasurementSource %d ", pulseCounterDriver.getMeasurement()); + return pulseCounterDriver.getMeasurement(); } } diff --git a/Software/STM32F103RBTx/Application/Device/Src/SdCardMeasurementRecorder.cpp b/Software/STM32F103RBTx/Application/Device/Src/SdCardMeasurementRecorder.cpp index bca19afb..5aeb3f4d 100644 --- a/Software/STM32F103RBTx/Application/Device/Src/SdCardMeasurementRecorder.cpp +++ b/Software/STM32F103RBTx/Application/Device/Src/SdCardMeasurementRecorder.cpp @@ -32,15 +32,13 @@ namespace Device return status; } - void SdCardMeasurementRecorder::write() + bool SdCardMeasurementRecorder::flush() { + return true; } - void SdCardMeasurementRecorder::flush() - { - } - - void SdCardMeasurementRecorder::notify() + bool SdCardMeasurementRecorder::notify(Device::MeasurementType &measurement) { + return true; } } diff --git a/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementRecorder.cpp b/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementRecorder.cpp index c219a472..bafd352b 100644 --- a/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementRecorder.cpp +++ b/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementRecorder.cpp @@ -8,16 +8,14 @@ namespace Device { } - void UartMeasurementRecorder::write() + bool UartMeasurementRecorder::flush() { + return true; } - void UartMeasurementRecorder::flush() - { - } - - void UartMeasurementRecorder::notify() + bool UartMeasurementRecorder::notify(Device::MeasurementType &measurement) { + return true; } } diff --git a/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementSource.cpp b/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementSource.cpp index 76726885..12a138e2 100644 --- a/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementSource.cpp +++ b/Software/STM32F103RBTx/Application/Device/Src/UartMeasurementSource.cpp @@ -8,13 +8,18 @@ namespace Device { } - bool UartMeasurementSource::init() + bool UartMeasurementSource::initialize() { const bool status = driver.initialize(); return status; } - bool UartMeasurementSource::deinit() + bool UartMeasurementSource::start() + { + return true; + } + + bool UartMeasurementSource::stop() { return true; } @@ -24,7 +29,8 @@ namespace Device return true; } - void UartMeasurementSource::getMeasurement() + MeasurementType UartMeasurementSource::getMeasurement() { + return (std::uint8_t)5; } } diff --git a/Software/STM32F103RBTx/Application/Device/Src/WiFiMeasurementRecorder.cpp b/Software/STM32F103RBTx/Application/Device/Src/WiFiMeasurementRecorder.cpp index 0c222885..d9ff46b5 100644 --- a/Software/STM32F103RBTx/Application/Device/Src/WiFiMeasurementRecorder.cpp +++ b/Software/STM32F103RBTx/Application/Device/Src/WiFiMeasurementRecorder.cpp @@ -3,7 +3,7 @@ // #include "Driver/Inc/UartExchangeStatus.hpp" #include - +#include namespace Device { @@ -35,35 +35,85 @@ namespace Device return status; } - void WiFiMeasurementRecorder::write() + bool WiFiMeasurementRecorder::write(Device::MeasurementType &measurement) { - /* - Driver::UartExchangeStatus status1, status2; + + printf("= START=, data %d\n", measurement); + + std::uint16_t len = 3; // TODO + + std::uint8_t data[6] = {0}; // Buffer to hold data + terminators + + // Use std::visit to safely extract the value from the variant + std::visit([&](auto &&value) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) { - // #define HAL_MAX_DELAY 0xFFFFFFFFU - // std::uint8_t data[] = {0xc, 0xa, 0xf, 0xe, '\r', '\n'}; - std::uint8_t data[] = {'z', 'r', '\r', '\n'}; - // char message[] = "r\r\n"; - auto len = sizeof(data) / sizeof(data[0]); - status1 = driver.transmit(data, len, Driver::IUartDriver::MaxDelay); // + data[0] = value; // Single byte + data[1] = '\r'; + data[2] = '\n'; + +printf("i am here 1, data %u\n", static_cast(data[0])); } + else if constexpr (std::is_same_v) { - std::uint8_t data_rx[30] = {0}; - auto len = 3; - status2 = driver.receive(data_rx, len, 0xFFFFU); - - volatile int a; + data[0] = static_cast((value >> 8) & 0xFF); // High byte + data[1] = static_cast(value & 0xFF); // Low byte + data[2] = '\r'; + data[3] = '\n'; + + printf("i am here 2\n"); } + else if constexpr (std::is_same_v) + { + data[0] = static_cast((value >> 24) & 0xFF); // Byte 3 + data[1] = static_cast((value >> 16) & 0xFF); // Byte 2 + data[2] = static_cast((value >> 8) & 0xFF); // Byte 1 + data[3] = static_cast(value & 0xFF); // Byte 0 + + data[4] = '\r'; + data[5] = '\n'; + + len = 5; + + printf("i am here 4\n"); + } }, measurement); + + printf("= STOP=, data %d\n", measurement); + + // Append terminator bytes + // data[sizeof(data) - 2] = '\r'; + // data[sizeof(data) - 1] = '\n'; + + // auto len = sizeof(data) / sizeof(data[0]); + // std::uint16_t len = 3; // hack for now + + driver.transmit(data, len, Driver::IUartDriver::MaxDelay); + + /* + std::uint8_t data_rx[30] = {0}; + auto len = 3; + status2 = driver.receive(data_rx, len, 0xFFFFU); + + volatile int a; + */ + + const bool status = true; + return status; } - void WiFiMeasurementRecorder::flush() + bool WiFiMeasurementRecorder::flush() { + const bool status = true; + return status; } - void WiFiMeasurementRecorder::notify() + bool WiFiMeasurementRecorder::notify(Device::MeasurementType &measurement) { - // dummy for debug - write(); + const bool status = write(measurement); + return status; } } diff --git a/Software/STM32F103RBTx/Application/Driver/Inc/PulseCounterDriver.hpp b/Software/STM32F103RBTx/Application/Driver/Inc/PulseCounterDriver.hpp index 0a8aeb00..77a68237 100644 --- a/Software/STM32F103RBTx/Application/Driver/Inc/PulseCounterDriver.hpp +++ b/Software/STM32F103RBTx/Application/Driver/Inc/PulseCounterDriver.hpp @@ -8,6 +8,11 @@ #include "Driver/Interfaces/IPulseCounterDriver.hpp" +extern "C" +{ + void incrementPulseCounter(std::uint8_t counterId); +} + namespace Driver { /** @@ -20,19 +25,17 @@ namespace Driver class PulseCounterDriver : public IPulseCounterDriver { public: - /** - * @brief Constructs a PulseCounterDriver object. - * - * The constructor sets up the PulseCounterDriver, preparing it for initialization. - */ - explicit PulseCounterDriver() = default; + static const std::uint8_t PULSE_COUNTER_AMOUNT = 4u; + + explicit PulseCounterDriver(PulseCounterIdentifier id); + PulseCounterDriver() = delete; /** * @brief Default destructor for PulseCounterDriver. * * Ensures proper cleanup of the driver. */ - ~PulseCounterDriver() override = default; + ~PulseCounterDriver() override; /** * @brief Deleted copy constructor to prevent copying. @@ -45,6 +48,9 @@ namespace Driver */ PulseCounterDriver &operator=(const PulseCounterDriver &) = delete; + IPulseCounterDriver::CounterSizeType getMeasurement() override; + void clearMeasurement() override; + /** * @brief Initializes the pulse counter driver. * @@ -76,6 +82,9 @@ namespace Driver * @return True if the driver reset successfully, false otherwise. */ bool onReset() override; + + PulseCounterIdentifier id; + CounterSizeType &value; }; } diff --git a/Software/STM32F103RBTx/Application/Driver/Interfaces/IPulseCounterDriver.hpp b/Software/STM32F103RBTx/Application/Driver/Interfaces/IPulseCounterDriver.hpp index dfa04196..97e6cc6c 100644 --- a/Software/STM32F103RBTx/Application/Driver/Interfaces/IPulseCounterDriver.hpp +++ b/Software/STM32F103RBTx/Application/Driver/Interfaces/IPulseCounterDriver.hpp @@ -7,6 +7,7 @@ #define IPulseCounterDriver_h #include "Driver/Inc/DriverState.hpp" +#include "Driver/Inc/PulseCounterIdentifier.hpp" namespace Driver { @@ -21,9 +22,8 @@ namespace Driver class IPulseCounterDriver : public DriverState { public: - /** - * @brief Default constructor for IPulseCounterDriver. - */ + using CounterSizeType = std::uint32_t; // Define a type alias for the max value of the counter (in bits) + explicit IPulseCounterDriver() = default; /** @@ -32,6 +32,9 @@ namespace Driver * Ensures proper cleanup of derived classes. */ ~IPulseCounterDriver() override = default; + + virtual CounterSizeType getMeasurement() = 0; + virtual void clearMeasurement() = 0; }; } diff --git a/Software/STM32F103RBTx/Application/Driver/Src/PulseCounterDriver.cpp b/Software/STM32F103RBTx/Application/Driver/Src/PulseCounterDriver.cpp index e04f714e..10ad5d27 100644 --- a/Software/STM32F103RBTx/Application/Driver/Src/PulseCounterDriver.cpp +++ b/Software/STM32F103RBTx/Application/Driver/Src/PulseCounterDriver.cpp @@ -1,14 +1,45 @@ #include "Driver/Inc/PulseCounterDriver.hpp" +#include +#include + +// Declare a std::array to store pulse counts +static std::array pulseCounters = {0}; + +// Expose the array pointer for C compatibility +extern "C" +{ + + // C-compatible function to increment a specific counter + void incrementPulseCounter(std::uint8_t counterId) + { + if (counterId < Driver::PulseCounterDriver::PULSE_COUNTER_AMOUNT) + { + pulseCounters[counterId]++; + } + } +} + namespace Driver { + PulseCounterDriver::PulseCounterDriver(PulseCounterIdentifier id) : value(pulseCounters[(int)id]) + { + } + + PulseCounterDriver::~PulseCounterDriver() + { + clearMeasurement(); + } + bool PulseCounterDriver::onInitialize() { + pulseCounters.fill(0u); return true; } bool PulseCounterDriver::onStart() { + pulseCounters.fill(0u); return true; } @@ -21,4 +52,15 @@ namespace Driver { return true; } + + IPulseCounterDriver::CounterSizeType PulseCounterDriver::getMeasurement() + { + return value; + } + + void PulseCounterDriver::clearMeasurement() + { + value = 0; + } + } diff --git a/Test/System/test_pulse_counter.py b/Test/System/test_pulse_counter.py new file mode 100644 index 00000000..8eb8e4fd --- /dev/null +++ b/Test/System/test_pulse_counter.py @@ -0,0 +1,24 @@ +import time +import logging +import sys +from pathlib import Path + +# Add the path to Simulation/FirmwarePCSimulator to sys.path +simulation_path = Path(__file__).parent.parent.parent / "Simulation" / "FirmwarePCSimulator" +sys.path.append(str(simulation_path)) + +from simulation import Simulation, SimulationKey + + +logger = logging.getLogger(__name__) + +def test_pulse_counter_start(assert_display_content): + """Test to validate iteration through a list using display content.""" + logger.info("Starting test: test_iterate_list") + + dut: Simulation = Simulation() + dut.start_firmware() + logger.info("Firmware started for the simulation.") + + time.sleep(0.1) + diff --git a/Test/Unit/BusinessLogic/test_MeasurementCoordinator.cpp b/Test/Unit/BusinessLogic/test_MeasurementCoordinator.cpp new file mode 100644 index 00000000..e1e7303b --- /dev/null +++ b/Test/Unit/BusinessLogic/test_MeasurementCoordinator.cpp @@ -0,0 +1,119 @@ +#include "BusinessLogic/Inc/MeasurementCoordinator.hpp" +#include "BusinessLogic/Inc/MeasurementDataStore.hpp" +#include "Device/Interfaces/IMeasurementSource.hpp" +#include +#include +#include +#include + +// Mock class for Device::IMeasurementSource +class MockMeasurementSource : public Device::IMeasurementSource +{ +public: + MOCK_METHOD(bool, initialize, (), (override)); + MOCK_METHOD(bool, start, (), (override)); + MOCK_METHOD(bool, isMeasurementAvailable, (), (override)); + MOCK_METHOD(Device::MeasurementType, getMeasurement, (), (override)); + MOCK_METHOD(bool, stop, (), (override)); +}; + +// Mock class for BusinessLogic::MeasurementDataStore +class MockMeasurementDataStore : public BusinessLogic::IMeasurementDataStore +{ +public: + MOCK_METHOD(bool, notifyObservers, (Device::MeasurementType measurement), (override)); + MOCK_METHOD(bool, addObserver, (Device::IMeasurementRecorder &), (override)); + MOCK_METHOD(bool, removeObserver, (Device::IMeasurementRecorder &), (override)); + MOCK_METHOD(bool, initialize, (), (override)); + MOCK_METHOD(bool, start, (), (override)); +}; + +// Test Fixture for MeasurementCoordinator +class MeasurementCoordinatorTest : public ::testing::Test +{ +protected: + MockMeasurementDataStore mockStorage; + MockMeasurementSource mockSource1; + MockMeasurementSource mockSource2; + BusinessLogic::MeasurementCoordinator *coordinator; + + void SetUp() override + { + coordinator = new BusinessLogic::MeasurementCoordinator(mockStorage); + } + + void TearDown() override + { + delete coordinator; + } +}; + +// Test initialize() method +TEST_F(MeasurementCoordinatorTest, InitializeShouldCallInitializeAndStartOnAllObservers) +{ + EXPECT_CALL(mockSource1, initialize()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockSource1, start()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockSource2, initialize()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockSource2, start()).WillOnce(::testing::Return(true)); + + coordinator->addObserver(mockSource1); + coordinator->addObserver(mockSource2); + + EXPECT_TRUE(coordinator->initialize()); +} + +// Test tick() method +TEST_F(MeasurementCoordinatorTest, TickShouldUpdateMeasurements) +{ + EXPECT_CALL(mockSource1, isMeasurementAvailable()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockSource1, getMeasurement()).WillOnce(::testing::Return(Device::MeasurementType{})); + EXPECT_CALL(mockStorage, notifyObservers(::testing::_)).Times(1); + + EXPECT_CALL(mockSource2, isMeasurementAvailable()).WillOnce(::testing::Return(false)); + EXPECT_CALL(mockSource2, getMeasurement()).Times(0); + + coordinator->addObserver(mockSource1); + coordinator->addObserver(mockSource2); + + EXPECT_TRUE(coordinator->tick()); +} + +// Test tick() when no observers are present +TEST_F(MeasurementCoordinatorTest, TickSucceedsWithNoObservers) +{ + EXPECT_TRUE(coordinator->tick()); +} + +// Test addObserver() method +TEST_F(MeasurementCoordinatorTest, AddObserverShouldAddObserversSuccessfully) +{ + EXPECT_TRUE(coordinator->addObserver(mockSource1)); + EXPECT_TRUE(coordinator->addObserver(mockSource2)); +} + +// Test removeObserver() method +TEST_F(MeasurementCoordinatorTest, RemoveObserverShouldRemoveObserversSuccessfully) +{ + coordinator->addObserver(mockSource1); + EXPECT_TRUE(coordinator->removeObserver(mockSource1)); +} + +// Test removeObserver() with non-existent observer +TEST_F(MeasurementCoordinatorTest, RemoveObserverFailsForNonExistentObserver) +{ + EXPECT_FALSE(coordinator->removeObserver(mockSource1)); +} + +// Test initialize() failure when one observer fails to initialize +TEST_F(MeasurementCoordinatorTest, InitializeFailsIfAnyObserverFailsToInitialize) +{ + EXPECT_CALL(mockSource1, initialize()).WillOnce(::testing::Return(false)); + EXPECT_CALL(mockSource1, start()).Times(0); + EXPECT_CALL(mockSource2, initialize()).Times(0); + EXPECT_CALL(mockSource2, start()).Times(0); + + coordinator->addObserver(mockSource1); + coordinator->addObserver(mockSource2); + + EXPECT_FALSE(coordinator->initialize()); +} diff --git a/Test/Unit/BusinessLogic/test_MeasurementDataStore.cpp b/Test/Unit/BusinessLogic/test_MeasurementDataStore.cpp new file mode 100644 index 00000000..685c4f0a --- /dev/null +++ b/Test/Unit/BusinessLogic/test_MeasurementDataStore.cpp @@ -0,0 +1,105 @@ +#include "BusinessLogic/Inc/MeasurementDataStore.hpp" +#include "Device/Interfaces/IMeasurementRecorder.hpp" +#include "Driver/Inc/UartExchangeStatus.hpp" +#include +#include + +// Mock class for Device::IMeasurementRecorder +class MockMeasurementRecorder : public Device::IMeasurementRecorder +{ +public: + MOCK_METHOD(bool, initialize, (), (override)); + MOCK_METHOD(bool, start, (), (override)); + MOCK_METHOD(bool, notify, (Device::MeasurementType & measurement), (override)); + MOCK_METHOD(bool, onInitialize, (), (override)); + MOCK_METHOD(bool, onStart, (), (override)); + MOCK_METHOD(bool, onStop, (), (override)); + MOCK_METHOD(bool, onReset, (), (override)); + MOCK_METHOD(bool, flush, (), (override)); +}; + +// Test Fixture for BusinessLogic::MeasurementDataStore +class MeasurementDataStoreTest : public ::testing::Test +{ +protected: + BusinessLogic::MeasurementDataStore *dataStore; + MockMeasurementRecorder mockRecorder1; + MockMeasurementRecorder mockRecorder2; + + void SetUp() override + { + dataStore = new BusinessLogic::MeasurementDataStore(); + } + + void TearDown() override + { + delete dataStore; + } +}; + +// Test initialize() method +TEST_F(MeasurementDataStoreTest, InitializeShouldCallInitializeOnAllObservers) +{ + EXPECT_CALL(mockRecorder1, initialize()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockRecorder2, initialize()).WillOnce(::testing::Return(true)); + + dataStore->addObserver(mockRecorder1); + dataStore->addObserver(mockRecorder2); + + EXPECT_TRUE(dataStore->initialize()); +} + +// Test start() method +TEST_F(MeasurementDataStoreTest, StartShouldCallStartOnAllObservers) +{ + EXPECT_CALL(mockRecorder1, start()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockRecorder2, start()).WillOnce(::testing::Return(true)); + + dataStore->addObserver(mockRecorder1); + dataStore->addObserver(mockRecorder2); + + EXPECT_TRUE(dataStore->start()); +} + +// Test addObserver() method +TEST_F(MeasurementDataStoreTest, AddObserverShouldAddObserverSuccessfully) +{ + EXPECT_TRUE(dataStore->addObserver(mockRecorder1)); + EXPECT_TRUE(dataStore->addObserver(mockRecorder2)); +} + +// Test removeObserver() method +TEST_F(MeasurementDataStoreTest, RemoveObserverShouldRemoveObserverSuccessfully) +{ + dataStore->addObserver(mockRecorder1); + EXPECT_TRUE(dataStore->removeObserver(mockRecorder1)); +} + +TEST_F(MeasurementDataStoreTest, NotifyObserversShouldCallNotifyOnAllObserversWithCorrectValue) +{ + Device::MeasurementType mockMeasurement{42u}; // The expected measurement value + + // Check that notify is called with the exact value 42u + EXPECT_CALL(mockRecorder1, notify(::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke([](Device::MeasurementType &measurement) -> bool + { + EXPECT_TRUE(std::holds_alternative(measurement)); + EXPECT_EQ(std::get(measurement), 42u); + return true; // Must return bool + })); + + EXPECT_CALL(mockRecorder2, notify(::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke([](Device::MeasurementType &measurement) -> bool + { + EXPECT_TRUE(std::holds_alternative(measurement)); + EXPECT_EQ(std::get(measurement), 42u); + return true; // Must return bool + })); + + dataStore->addObserver(mockRecorder1); + dataStore->addObserver(mockRecorder2); + + dataStore->notifyObservers(mockMeasurement); +} diff --git a/Test/Unit/CMakeLists.txt b/Test/Unit/CMakeLists.txt index b8fbedb8..7e8d374f 100644 --- a/Test/Unit/CMakeLists.txt +++ b/Test/Unit/CMakeLists.txt @@ -105,6 +105,45 @@ set(TEST_UART_SRC ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp ) +set(PULSE_COUNTER_DRIVER_SRC + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/PulseCounterDriver.cpp + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/DriverState.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/Driver/test_PulseCounterDriver.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp +) + +set(PULSE_COUNTER_MEASUREMENT_SOURCE_SRC + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Device/Src/PulseCounterMeasurementSource.cpp + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/DriverState.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/Device/test_PulseCounterMeasurementSource.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp +) + + +set(MEASUREMENT_DATA_STORE_SRC + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementDataStore.cpp + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/DriverState.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/BusinessLogic/test_MeasurementDataStore.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp +) + +set(TEST_MEASUREMENT_COORDINATOR + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/BusinessLogic/Src/MeasurementCoordinator.cpp + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/DriverState.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/BusinessLogic/test_MeasurementCoordinator.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp +) + + +set(WIFI_MEASUREMENT_RECORDER + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Device/Src/WiFiMeasurementRecorder.cpp + ${CMAKE_SOURCE_DIR}/Software/STM32F103RBTx/Application/Driver/Src/DriverState.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/Device/test_WiFiMeasurementRecorder.cpp + ${CMAKE_SOURCE_DIR}/Test/Unit/System/main.cpp +) + + + # Add test executables using the create_test_exec function with source lists create_test_exec(test_SaferArray "${TEST_SAFERARRAY_SRC}") create_test_exec(test_DriverState "${TEST_DRIVERSTATE_SRC}") @@ -113,6 +152,11 @@ create_test_exec(test_AmbientLightSensorDriver "${TEST_AMBIENTLIGHT_SRC}") create_test_exec(test_DisplayPixelColor "${TEST_DISPLAYPIXELCOLOR_SRC}") create_test_exec(test_St7735DisplayBrightnessDriver "${TEST_ST7735BRIGHTNESS_SRC}") create_test_exec(test_UartDriver "${TEST_UART_SRC}") +create_test_exec(test_PulseCounterDriver "${PULSE_COUNTER_DRIVER_SRC}") +create_test_exec(test_PulseCounterMeasurementSource "${PULSE_COUNTER_MEASUREMENT_SOURCE_SRC}") +create_test_exec(test_MeasurementDataStore "${MEASUREMENT_DATA_STORE_SRC}") +create_test_exec(test_MeasurementCoordinator "${TEST_MEASUREMENT_COORDINATOR}") +create_test_exec(test_WiFiMeasurementRecorder "${WIFI_MEASUREMENT_RECORDER}") # Custom target to run all tests add_custom_target(test diff --git a/Test/Unit/Device/test_PulseCounterMeasurementSource.cpp b/Test/Unit/Device/test_PulseCounterMeasurementSource.cpp new file mode 100644 index 00000000..ce8086aa --- /dev/null +++ b/Test/Unit/Device/test_PulseCounterMeasurementSource.cpp @@ -0,0 +1,79 @@ +#include "Device/Inc/PulseCounterMeasurementSource.hpp" +#include "Driver/Interfaces/IPulseCounterDriver.hpp" +#include +#include +#include +#include +#include + +constexpr Driver::PulseCounterIdentifier id = Driver::PulseCounterIdentifier::bncA; + +// Mock class for IPulseCounterDriver +class MockPulseCounterDriver : public Driver::IPulseCounterDriver +{ +public: + MOCK_METHOD(Driver::IPulseCounterDriver::CounterSizeType, getMeasurement, (), (override)); + MOCK_METHOD(void, clearMeasurement, (), (override)); + MOCK_METHOD(bool, onInitialize, (), (override)); + MOCK_METHOD(bool, onStart, (), (override)); + MOCK_METHOD(bool, onStop, (), (override)); + MOCK_METHOD(bool, onReset, (), (override)); +}; + +// Test Fixture for PULSE_COUNTER_MEASUREMENT_SOURCE +class PULSE_COUNTER_MEASUREMENT_SOURCE_TEST : public ::testing::Test +{ +protected: + MockPulseCounterDriver mockDriver; + Device::PulseCounterMeasurementSource *measurementSource; + + void SetUp() override + { + measurementSource = new Device::PulseCounterMeasurementSource(mockDriver); + } + + void TearDown() override + { + delete measurementSource; + } +}; + +// Test initialize() method +TEST_F(PULSE_COUNTER_MEASUREMENT_SOURCE_TEST, InitializeShouldCallDriverInitialize) +{ + EXPECT_CALL(mockDriver, onInitialize()).Times(1).WillOnce(::testing::Return(true)); + EXPECT_TRUE(measurementSource->initialize()); +} + +// Test start() method +TEST_F(PULSE_COUNTER_MEASUREMENT_SOURCE_TEST, StartShouldCallDriverStart) +{ + EXPECT_CALL(mockDriver, onInitialize()).Times(1).WillOnce(::testing::Return(true)); + EXPECT_TRUE(measurementSource->initialize()); + + EXPECT_CALL(mockDriver, onStart()).Times(1).WillOnce(::testing::Return(true)); + EXPECT_TRUE(measurementSource->start()); +} + +// Test isMeasurementAvailable() method +TEST_F(PULSE_COUNTER_MEASUREMENT_SOURCE_TEST, IsMeasurementAvailableReturnsTrue) +{ + EXPECT_TRUE(measurementSource->isMeasurementAvailable()); +} + +// Test getMeasurement() method +TEST_F(PULSE_COUNTER_MEASUREMENT_SOURCE_TEST, GetMeasurementShouldReturnDriverValue) +{ + constexpr Driver::IPulseCounterDriver::CounterSizeType mockValue = static_cast(42); + EXPECT_CALL(mockDriver, getMeasurement()).Times(1).WillOnce(::testing::Return(mockValue)); + Device::MeasurementType measurement = measurementSource->getMeasurement(); + EXPECT_EQ(std::get(measurement), mockValue); +} + +// Test clearMeasurement() method +TEST_F(PULSE_COUNTER_MEASUREMENT_SOURCE_TEST, ClearMeasurementShouldCallDriverClear) +{ + EXPECT_CALL(mockDriver, clearMeasurement()).Times(1); + measurementSource->getMeasurement(); + mockDriver.clearMeasurement(); +} diff --git a/Test/Unit/Device/test_WiFiMeasurementRecorder.cpp b/Test/Unit/Device/test_WiFiMeasurementRecorder.cpp new file mode 100644 index 00000000..44e31f45 --- /dev/null +++ b/Test/Unit/Device/test_WiFiMeasurementRecorder.cpp @@ -0,0 +1,114 @@ +#include "Device/Inc/WiFiMeasurementRecorder.hpp" +#include "Driver/Interfaces/IUartDriver.hpp" +#include "Driver/Inc/UartExchangeStatus.hpp" +#include +#include +#include +#include // For memcmp + +// Mock class for IUartDriver +class MockUartDriver : public Driver::IUartDriver +{ +public: + MOCK_METHOD(bool, onInitialize, (), (override)); + MOCK_METHOD(bool, onStart, (), (override)); + MOCK_METHOD(bool, onStop, (), (override)); + MOCK_METHOD(bool, onReset, (), (override)); + MOCK_METHOD(Driver::UartExchangeStatus, transmit, (std::uint8_t * data, std::uint16_t size, std::uint32_t timeout), (override)); + MOCK_METHOD(Driver::UartExchangeStatus, receive, (std::uint8_t * data, std::uint16_t size, std::uint32_t timeout), (override)); +}; + +// Test Fixture for WiFiMeasurementRecorder +class WiFiMeasurementRecorderTest : public ::testing::Test +{ +protected: + MockUartDriver mockDriver; + Device::WiFiMeasurementRecorder *recorder; + + void SetUp() override + { + recorder = new Device::WiFiMeasurementRecorder(mockDriver); + } + + void TearDown() override + { + delete recorder; + } +}; + +// Test onInitialize() +TEST_F(WiFiMeasurementRecorderTest, OnInitializeCallsDriverInitialize) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->initialize()); +} + +TEST_F(WiFiMeasurementRecorderTest, OnInitializeFailsIfDriverInitializeFails) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(false)); + EXPECT_FALSE(recorder->initialize()); +} + +// Test onStart() +TEST_F(WiFiMeasurementRecorderTest, OnStartCallsDriverStart) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->initialize()); + + EXPECT_CALL(mockDriver, onStart()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->start()); +} + +TEST_F(WiFiMeasurementRecorderTest, OnStartFailsIfDriverStartFails) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->initialize()); + + EXPECT_CALL(mockDriver, onStart()).WillOnce(::testing::Return(false)); + EXPECT_FALSE(recorder->start()); +} + +// Test onStop() +TEST_F(WiFiMeasurementRecorderTest, OnStopCallsDriverStop) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->initialize()); + + EXPECT_CALL(mockDriver, onStart()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->start()); + + EXPECT_CALL(mockDriver, onStop()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->stop()); +} + +TEST_F(WiFiMeasurementRecorderTest, OnStopFailsIfDriverStopFails) +{ + EXPECT_CALL(mockDriver, onInitialize()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->initialize()); + + EXPECT_CALL(mockDriver, onStart()).WillOnce(::testing::Return(true)); + EXPECT_TRUE(recorder->start()); + + EXPECT_CALL(mockDriver, onStop()).WillOnce(::testing::Return(false)); + EXPECT_FALSE(recorder->stop()); +} + +// Test notify() calls write() +TEST_F(WiFiMeasurementRecorderTest, NotifyCallsWrite) +{ + // TOD rework this test + // Device::MeasurementType inputData{42u}; // The expected measurement value + std::uint8_t data = 5; + Device::MeasurementType d(data); + // Using EXPECT_CALL to capture the behavior within write() + std::uint8_t expectedData[] = {data, '\r', '\n'}; + + EXPECT_CALL(mockDriver, transmit(::testing::_, sizeof(expectedData), Driver::IUartDriver::MaxDelay)) + .WillOnce([&](std::uint8_t *data, std::uint16_t size, std::uint32_t) + { + EXPECT_EQ(size, sizeof(expectedData)); + EXPECT_EQ(std::memcmp(data, expectedData, size), 0) << "Transmitted data does not match expected content"; + return Driver::UartExchangeStatus::Ok; }); + + recorder->notify(d); +} diff --git a/Test/Unit/Driver/test_PulseCounterDriver.cpp b/Test/Unit/Driver/test_PulseCounterDriver.cpp new file mode 100644 index 00000000..4c7546c4 --- /dev/null +++ b/Test/Unit/Driver/test_PulseCounterDriver.cpp @@ -0,0 +1,57 @@ +#include +#include +#include "Driver/Inc/PulseCounterDriver.hpp" +#include +#include + +constexpr Driver::PulseCounterIdentifier id = Driver::PulseCounterIdentifier::bncA; + +class PulseCounterDriverTest : public ::testing::Test +{ +protected: + Driver::PulseCounterDriver *driver; + + void SetUp() override + { + driver = new Driver::PulseCounterDriver(static_cast(id)); + } + + void TearDown() override + { + delete driver; + } +}; + +TEST_F(PulseCounterDriverTest, InitializeShouldResetCounters) +{ + EXPECT_TRUE(driver->onInitialize()); + EXPECT_EQ(driver->getMeasurement(), 0u); +} + +TEST_F(PulseCounterDriverTest, StartShouldResetCounters) +{ + EXPECT_TRUE(driver->onStart()); + EXPECT_EQ(driver->getMeasurement(), 0u); +} + +TEST_F(PulseCounterDriverTest, IncrementCounterIncreasesCorrectValue) +{ + auto idOfCounter = static_cast(id); + + incrementPulseCounter(idOfCounter); + EXPECT_EQ(driver->getMeasurement(), 1u); + + incrementPulseCounter(idOfCounter); + EXPECT_EQ(driver->getMeasurement(), 2u); +} + +TEST_F(PulseCounterDriverTest, ClearMeasurementResetsCounter) +{ + auto idOfCounter = static_cast(id); + + incrementPulseCounter(idOfCounter); + EXPECT_EQ(driver->getMeasurement(), 1u); + + driver->clearMeasurement(); + EXPECT_EQ(driver->getMeasurement(), 0u); +}