From aff9e626899dec8dba3ce28a35f6254439fbb233 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Thu, 28 Dec 2023 12:34:20 +0100 Subject: [PATCH 01/21] Basic impl. with a bugs --- .../lib/RemoteControl/RemoteControl.cpp | 8 +++ .../lib/RemoteControl/RemoteControl.h | 1 + .../tea_poor/lib/WaterPump/IWaterPump.h | 15 ++++ .../WaterPumpController.cpp | 19 ++--- .../lib/WaterPump/WaterPumpController.h | 23 +++++++ .../lib/WaterPump/WaterPumpScheduler.cpp | 36 ++++++++++ .../lib/WaterPump/WaterPumpScheduler.h | 33 +++++++++ .../WaterPumpController/WaterPumpController.h | 28 -------- controller/tea_poor/platformio.ini | 5 ++ controller/tea_poor/src/main.cpp | 60 +++++++++++++--- .../test_local/WaterPumpScheduler_test.cpp | 69 +++++++++++++++++++ 11 files changed, 245 insertions(+), 52 deletions(-) create mode 100644 controller/tea_poor/lib/WaterPump/IWaterPump.h rename controller/tea_poor/lib/{WaterPumpController => WaterPump}/WaterPumpController.cpp (66%) create mode 100644 controller/tea_poor/lib/WaterPump/WaterPumpController.h create mode 100644 controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp create mode 100644 controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h delete mode 100644 controller/tea_poor/lib/WaterPumpController/WaterPumpController.h create mode 100644 controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp b/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp index c4ebe9d..16c3ec9 100644 --- a/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp +++ b/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp @@ -95,4 +95,12 @@ void RemoteControl::process() { _app.process(&client); client.stop(); } +} + +String RemoteControl::asJSONString() const { + String result = "{"; + result += "\"SSID\": \"" + _SSID + "\","; + result += "\"signal strength\": " + String(WiFi.RSSI()); + result += "}"; + return result; } \ No newline at end of file diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.h b/controller/tea_poor/lib/RemoteControl/RemoteControl.h index 78fb5e5..1447248 100644 --- a/controller/tea_poor/lib/RemoteControl/RemoteControl.h +++ b/controller/tea_poor/lib/RemoteControl/RemoteControl.h @@ -14,6 +14,7 @@ class RemoteControl { ~RemoteControl(); void setup(RemoteControlRoutesCallback routes); void process(); + String asJSONString() const; private: const String _SSID; const String _SSIDPassword; diff --git a/controller/tea_poor/lib/WaterPump/IWaterPump.h b/controller/tea_poor/lib/WaterPump/IWaterPump.h new file mode 100644 index 0000000..4377980 --- /dev/null +++ b/controller/tea_poor/lib/WaterPump/IWaterPump.h @@ -0,0 +1,15 @@ +#ifndef IWATERPUMP_H +#define IWATERPUMP_H + +class IWaterPump { +public: + virtual ~IWaterPump() {} + + virtual void setup() = 0; + virtual void start() = 0; + virtual void stop() = 0; + + virtual bool isRunning() const = 0; +}; + +#endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp b/controller/tea_poor/lib/WaterPump/WaterPumpController.cpp similarity index 66% rename from controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp rename to controller/tea_poor/lib/WaterPump/WaterPumpController.cpp index b8ce60f..25768ec 100644 --- a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp +++ b/controller/tea_poor/lib/WaterPump/WaterPumpController.cpp @@ -15,24 +15,17 @@ void WaterPumpController::setup() { pinMode(_directionPin, OUTPUT); pinMode(_brakePin, OUTPUT); pinMode(_powerPin, OUTPUT); - // TODO: check that its okay to do during setup - stopPump(); + stop(); } -void WaterPumpController::pour(int milliseconds) { - startPump(); - delay(milliseconds); - stopPump(); -} - -void WaterPumpController::startPump() { - _state = PUMP_ON; +void WaterPumpController::start() { + _isRunning = true; digitalWrite(_brakePin, LOW); // release breaks analogWrite(_powerPin, 255); } -void WaterPumpController::stopPump() { +void WaterPumpController::stop() { digitalWrite(_brakePin, HIGH); // activate breaks analogWrite(_powerPin, 0); - _state = PUMP_OFF; -} + _isRunning = false; +} \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpController.h b/controller/tea_poor/lib/WaterPump/WaterPumpController.h new file mode 100644 index 0000000..511560c --- /dev/null +++ b/controller/tea_poor/lib/WaterPump/WaterPumpController.h @@ -0,0 +1,23 @@ +#ifndef WATERPUMPCONTROLLER_H +#define WATERPUMPCONTROLLER_H +#include "IWaterPump.h" + +class WaterPumpController: public IWaterPump { +private: + const int _directionPin; + const int _brakePin; + const int _powerPin; + const int _maxPower = 255; + bool _isRunning = false; +public: + WaterPumpController(int directionPin, int brakePin, int powerPin); + virtual ~WaterPumpController() override; + + virtual void setup() override; + virtual void start() override; + virtual void stop() override; + + virtual bool isRunning() const override { return _isRunning; } +}; + +#endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp new file mode 100644 index 0000000..21bb9fe --- /dev/null +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp @@ -0,0 +1,36 @@ +#include "WaterPumpScheduler.h" + +WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) { +} + +WaterPumpScheduler::~WaterPumpScheduler() { + delete _waterPump; +} + +void WaterPumpScheduler::setup() { + _waterPump->setup(); +} + +void WaterPumpScheduler::start(unsigned long runTimeMs, unsigned long currentTimeMs) { + _stopTime = currentTimeMs + runTimeMs; + _waterPump->start(); +} + +void WaterPumpScheduler::stop() { + _waterPump->stop(); + _stopTime = 0; // a bit of paranoia :) +} + +void WaterPumpScheduler::tick(unsigned long currentTimeMs) { + if (_stopTime <= currentTimeMs) { + stop(); + _stopTime = currentTimeMs + _forceStopIntervalMs; // force stop after X milliseconds + } +} + +WaterPumpScheduler::WaterPumpStatus WaterPumpScheduler::status() { + return { + _waterPump->isRunning(), + _stopTime + }; +} \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h new file mode 100644 index 0000000..6abb21f --- /dev/null +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h @@ -0,0 +1,33 @@ +#ifndef WATERPUMPSCHEDULER_H +#define WATERPUMPSCHEDULER_H + +#include "IWaterPump.h" + +// This class is responsible for scheduling water pump +// It is used to make sure that water pump is running for a limited time +// It is also ensuring that water pump is stopped if not needed +class WaterPumpScheduler { +private: + IWaterPump* _waterPump; + unsigned long _stopTime = 0; + // each X milliseconds will force stop water pump + unsigned long _forceStopIntervalMs; +public: + WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs); + WaterPumpScheduler(IWaterPump* waterPump) : WaterPumpScheduler(waterPump, 1000) {} + ~WaterPumpScheduler(); + + void setup(); + void stop(); + // for simplicity and testability we are passing current time as parameter + void start(unsigned long runTimeMs, unsigned long currentTimeMs); + void tick(unsigned long currentTimeMs); + + // pump status + struct WaterPumpStatus { + bool isRunning; + unsigned long stopTime; + }; + WaterPumpStatus status(); +}; +#endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h deleted file mode 100644 index 8561876..0000000 --- a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef WATERPUMPCONTROLLER_H -#define WATERPUMPCONTROLLER_H - -class WaterPumpController { -public: - enum EPumpState { - PUMP_OFF, - PUMP_ON - }; -private: - const int _directionPin; - const int _brakePin; - const int _powerPin; - const int _maxPower = 255; - EPumpState _state = PUMP_OFF; -public: - WaterPumpController(int directionPin, int brakePin, int powerPin); - ~WaterPumpController(); - - void setup(); - void pour(int miliseconds); - void startPump(); - void stopPump(); - - EPumpState state() const { return _state; } -}; - -#endif // WATERPUMPCONTROLLER_H diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 1515112..ca12eab 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -14,3 +14,8 @@ board = uno_r4_wifi framework = arduino lib_deps = lasselukkari/aWOT@^3.5.0 +test_ignore = local + +[env:local] +platform = native +test_framework = googletest \ No newline at end of file diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index 87b8358..82747dd 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -1,9 +1,12 @@ #include #include +#include #include // Setting up water pump -WaterPumpController waterPumpController(12, 9, 3); +WaterPumpScheduler waterPump( + new WaterPumpController(12, 9, 3) +); // Just for safety reasons, we don't want to pour tea for too long // Their is no reason to make it configurable and add unnecessary complexity const int WATER_PUMP_SAFE_THRESHOLD = 10 * 1000; @@ -14,8 +17,38 @@ RemoteControl remoteControl( "VerySecurePassword" // network password ); +void _sendSystemStatus(Response &res) { + // send system status as JSON + res.println("{"); + // send water threshold + res.print("\"water threshold\": "); + res.print(WATER_PUMP_SAFE_THRESHOLD); + res.println(","); + + // send water pump status + const auto waterPumpStatus = waterPump.status(); + res.println("\"pump\": {"); + res.print("\"running\": "); + res.print(waterPumpStatus.isRunning ? "true, " : "false, "); + const unsigned long timeLeft = + waterPumpStatus.isRunning ? + waterPumpStatus.stopTime - millis() : + 0; + res.print("\"time left\": "); + res.print(timeLeft); + res.println("},"); + // end of water pump status + /////////////////////////////////// + // send remote control status + res.print("\"remote control\": "); + res.print(remoteControl.asJSONString()); + res.println(); + // end of JSON + res.println("}"); +} + bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) { - if (strlen(str) <= 0) return false; + if (strlen(str) < 1) return false; const int value = atoi(str); if (value < minValue) return false; if (maxValue <= value) return false; @@ -31,24 +64,29 @@ void pour_tea(Request &req, Response &res) { res.println(WATER_PUMP_SAFE_THRESHOLD); return; } - const int pouringDelayMs = atoi(milliseconds); - // actually pour tea - waterPumpController.pour(pouringDelayMs); - - // Serial.println(req.JSON()); - res.print("Poured Tea in: "); - res.print(pouringDelayMs); - res.print(" milliseconds!"); + // start pouring tea + waterPump.start( atoi(milliseconds), millis() ); + _sendSystemStatus(res); } void setup() { Serial.begin(9600); - waterPumpController.setup(); + waterPump.setup(); remoteControl.setup([](Application &app) { app.get("/pour_tea", pour_tea); + // stop water pump + app.get("/stop", [](Request &req, Response &res) { + waterPump.stop(); + _sendSystemStatus(res); + }); + // get system status + app.get("/status", [](Request &req, Response &res) { + _sendSystemStatus(res); + }); }); } void loop() { + waterPump.tick(millis()); remoteControl.process(); }; \ No newline at end of file diff --git a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp new file mode 100644 index 0000000..61d75c6 --- /dev/null +++ b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp @@ -0,0 +1,69 @@ +// I wasn't able to run tests at all. Run them locally and confirm that they are working. +// Its either a local problem or a problem with the configuration of the project. +// Further goes a sketch of the tests, but I wasn't able to run them. +#include +#include + +// Fake water pump +class FakeWaterPump : public IWaterPump { +private: + bool _isRunning = false; +public: + void setup() override { _isRunning = false; } + void start() override { _isRunning = true; } + void stop() override { _isRunning = false; } + + bool isRunning() override { return _isRunning; } +}; +// End of fake water pump + +// test that pump is stopping after given time +TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { + // random time between 1 and 10 seconds + const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000; + FakeWaterPump fakeWaterPump; + WaterPumpScheduler waterPumpScheduler(&fakeWaterPump); + waterPumpScheduler.setup(); + // start water pump + unsigned long currentTimeMs = 0; + waterPumpScheduler.start(runTimeMs, currentTimeMs); + // check status + auto status = waterPumpScheduler.status(); + ASSERT_TRUE(status.isRunning); + ASSERT_EQ(status.stopTime, runTimeMs); + + while (currentTimeMs < runTimeMs) { + waterPumpScheduler.tick(currentTimeMs); + ASSERT_TRUE(fakeWaterPump->isRunning()); + currentTimeMs += 100; + } + // pump should be stopped after given time + waterPumpScheduler.tick(runTimeMs + 1); + ASSERT_FALSE(fakeWaterPump->isRunning()); +} + +// test that pump is periodically forced to stop after given time +TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) { + FakeWaterPump fakeWaterPump; + WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second + waterPumpScheduler.setup(); + // start water pump + unsigned long currentTimeMs = 0; + waterPumpScheduler.start(1, currentTimeMs); + currentTimeMs += 1; + waterPumpScheduler.tick(currentTimeMs); + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped after given time + + for(int i = 0; i < 10; i++) { + // emulate that pump was started again + fakeWaterPump.start(); + currentTimeMs += 1000; + waterPumpScheduler.tick(currentTimeMs); + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From f54a37559d99e05dec32564ac8498e994e391132 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Thu, 28 Dec 2023 13:15:38 +0100 Subject: [PATCH 02/21] would this fix CI? --- controller/tea_poor/platformio.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index ca12eab..6accfa8 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -18,4 +18,6 @@ test_ignore = local [env:local] platform = native -test_framework = googletest \ No newline at end of file +; test_framework = googletest +test_build_src = yes +build_src_filter = +*_test.cpp \ No newline at end of file From c8c2e45b268cadf6d3551794d0436e0f2871256c Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Thu, 28 Dec 2023 13:27:31 +0100 Subject: [PATCH 03/21] maybe now? --- controller/tea_poor/platformio.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 6accfa8..912817c 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -18,6 +18,6 @@ test_ignore = local [env:local] platform = native -; test_framework = googletest -test_build_src = yes -build_src_filter = +*_test.cpp \ No newline at end of file +test_framework = googletest +test_build_src = no +build_src_filter = -<*.cpp> +<*_test.cpp> \ No newline at end of file From c6003dc1614cb296c3e6767afe6eda05e97391c1 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Thu, 28 Dec 2023 13:31:20 +0100 Subject: [PATCH 04/21] last try --- controller/tea_poor/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 912817c..2999ffc 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -20,4 +20,4 @@ test_ignore = local platform = native test_framework = googletest test_build_src = no -build_src_filter = -<*.cpp> +<*_test.cpp> \ No newline at end of file +build_src_filter = +*.cpp -*_test.cpp \ No newline at end of file From 815c496f65fc3b96329cab3879faa4c5235ad643 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Thu, 28 Dec 2023 14:02:55 +0100 Subject: [PATCH 05/21] pls? --- controller/tea_poor/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 2999ffc..59d09e1 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -20,4 +20,4 @@ test_ignore = local platform = native test_framework = googletest test_build_src = no -build_src_filter = +*.cpp -*_test.cpp \ No newline at end of file +lib_ldf_mode = deep \ No newline at end of file From 54b8e582586a929a48508291520cebc0a021cdad Mon Sep 17 00:00:00 2001 From: omonrise Date: Fri, 29 Dec 2023 16:21:59 +0100 Subject: [PATCH 06/21] remove local and attempt to switch to unity framework --- controller/tea_poor/platformio.ini | 7 +- .../test_local/WaterPumpScheduler_test.cpp | 41 ++- .../tea_poor/test/test_local/unity_config.h | 244 ++++++++++++++++++ 3 files changed, 274 insertions(+), 18 deletions(-) create mode 100644 controller/tea_poor/test/test_local/unity_config.h diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 59d09e1..e711024 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -12,12 +12,9 @@ platform = renesas-ra board = uno_r4_wifi framework = arduino +test_framework = unity lib_deps = lasselukkari/aWOT@^3.5.0 + throwtheswitch/Unity@^2.5.2 test_ignore = local -[env:local] -platform = native -test_framework = googletest -test_build_src = no -lib_ldf_mode = deep \ No newline at end of file diff --git a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp index 61d75c6..dc43712 100644 --- a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp @@ -1,9 +1,11 @@ // I wasn't able to run tests at all. Run them locally and confirm that they are working. // Its either a local problem or a problem with the configuration of the project. // Further goes a sketch of the tests, but I wasn't able to run them. -#include +#include #include + + // Fake water pump class FakeWaterPump : public IWaterPump { private: @@ -13,12 +15,18 @@ class FakeWaterPump : public IWaterPump { void start() override { _isRunning = true; } void stop() override { _isRunning = false; } - bool isRunning() override { return _isRunning; } + bool isRunning() const override { return _isRunning; } }; // End of fake water pump + +/* Empty functions required by unity framework*/ +void setUp() { /* Setup code here */ } +void tearDown() { /* Teardown code here */ } + + // test that pump is stopping after given time -TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { +void test_pump_stops_after_given_time() { // random time between 1 and 10 seconds const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000; FakeWaterPump fakeWaterPump; @@ -29,21 +37,21 @@ TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { waterPumpScheduler.start(runTimeMs, currentTimeMs); // check status auto status = waterPumpScheduler.status(); - ASSERT_TRUE(status.isRunning); - ASSERT_EQ(status.stopTime, runTimeMs); + UNITY_TEST_ASSERT_TRUE(status.isRunning); + UNITY_TEST_ASSERT_EQUAL(status.stopTime, runTimeMs); while (currentTimeMs < runTimeMs) { waterPumpScheduler.tick(currentTimeMs); - ASSERT_TRUE(fakeWaterPump->isRunning()); + UNITY_TEST_ASSERT_TRUE(fakeWaterPump.isRunning()); currentTimeMs += 100; } // pump should be stopped after given time waterPumpScheduler.tick(runTimeMs + 1); - ASSERT_FALSE(fakeWaterPump->isRunning()); + UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); } // test that pump is periodically forced to stop after given time -TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) { +void test_pump_is_periodically_forced_to_stop_after_given_time () { FakeWaterPump fakeWaterPump; WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second waterPumpScheduler.setup(); @@ -52,18 +60,25 @@ TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_ti waterPumpScheduler.start(1, currentTimeMs); currentTimeMs += 1; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped after given time + UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time for(int i = 0; i < 10; i++) { // emulate that pump was started again fakeWaterPump.start(); currentTimeMs += 1000; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped + UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped } } -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +void setup() { + UNITY_BEGIN(); + RUN_TEST(test_pump_stops_after_given_time); + RUN_TEST(test_pump_is_periodically_forced_to_stop_after_given_time); + UNITY_END(); +} + + +void loop() { + } \ No newline at end of file diff --git a/controller/tea_poor/test/test_local/unity_config.h b/controller/tea_poor/test/test_local/unity_config.h new file mode 100644 index 0000000..f45e802 --- /dev/null +++ b/controller/tea_poor/test/test_local/unity_config.h @@ -0,0 +1,244 @@ +/* Unity Configuration + * As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529 + * Update: December 29th, 2016 + * See Also: Unity/docs/UnityConfigurationGuide.pdf + * + * Unity is designed to run on almost anything that is targeted by a C compiler. + * It would be awesome if this could be done with zero configuration. While + * there are some targets that come close to this dream, it is sadly not + * universal. It is likely that you are going to need at least a couple of the + * configuration options described in this document. + * + * All of Unity's configuration options are `#defines`. Most of these are simple + * definitions. A couple are macros with arguments. They live inside the + * unity_internals.h header file. We don't necessarily recommend opening that + * file unless you really need to. That file is proof that a cross-platform + * library is challenging to build. From a more positive perspective, it is also + * proof that a great deal of complexity can be centralized primarily to one + * place in order to provide a more consistent and simple experience elsewhere. + * + * Using These Options + * It doesn't matter if you're using a target-specific compiler and a simulator + * or a native compiler. In either case, you've got a couple choices for + * configuring these options: + * + * 1. Because these options are specified via C defines, you can pass most of + * these options to your compiler through command line compiler flags. Even + * if you're using an embedded target that forces you to use their + * overbearing IDE for all configuration, there will be a place somewhere in + * your project to configure defines for your compiler. + * 2. You can create a custom `unity_config.h` configuration file (present in + * your toolchain's search paths). In this file, you will list definitions + * and macros specific to your target. All you must do is define + * `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any + * further definitions it may need. + */ + +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +/* ************************* AUTOMATIC INTEGER TYPES *************************** + * C's concept of an integer varies from target to target. The C Standard has + * rules about the `int` matching the register size of the target + * microprocessor. It has rules about the `int` and how its size relates to + * other integer types. An `int` on one target might be 16 bits while on another + * target it might be 64. There are more specific types in compilers compliant + * with C99 or later, but that's certainly not every compiler you are likely to + * encounter. Therefore, Unity has a number of features for helping to adjust + * itself to match your required integer sizes. It starts off by trying to do it + * automatically. + **************************************************************************** */ + +/* The first attempt to guess your types is to check `limits.h`. Some compilers + * that don't support `stdint.h` could include `limits.h`. If you don't + * want Unity to check this file, define this to make it skip the inclusion. + * Unity looks at UINT_MAX & ULONG_MAX, which were available since C89. + */ +/* #define UNITY_EXCLUDE_LIMITS_H */ + +/* The second thing that Unity does to guess your types is check `stdint.h`. + * This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to + * learn about your system. It's possible you don't want it to do this or it's + * possible that your system doesn't support `stdint.h`. If that's the case, + * you're going to want to define this. That way, Unity will know to skip the + * inclusion of this file and you won't be left with a compiler error. + */ +/* #define UNITY_EXCLUDE_STDINT_H */ + +/* ********************** MANUAL INTEGER TYPE DEFINITION *********************** + * If you've disabled all of the automatic options above, you're going to have + * to do the configuration yourself. There are just a handful of defines that + * you are going to specify if you don't like the defaults. + **************************************************************************** */ + + /* Define this to be the number of bits an `int` takes up on your system. The + * default, if not auto-detected, is 32 bits. + * + * Example: + */ +/* #define UNITY_INT_WIDTH 16 */ + +/* Define this to be the number of bits a `long` takes up on your system. The + * default, if not autodetected, is 32 bits. This is used to figure out what + * kind of 64-bit support your system can handle. Does it need to specify a + * `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option + * is going to be ignored. + * + * Example: + */ +/* #define UNITY_LONG_WIDTH 16 */ + +/* Define this to be the number of bits a pointer takes up on your system. The + * default, if not autodetected, is 32-bits. If you're getting ugly compiler + * warnings about casting from pointers, this is the one to look at. + * + * Example: + */ +/* #define UNITY_POINTER_WIDTH 64 */ + +/* Unity will automatically include 64-bit support if it auto-detects it, or if + * your `int`, `long`, or pointer widths are greater than 32-bits. Define this + * to enable 64-bit support if none of the other options already did it for you. + * There can be a significant size and speed impact to enabling 64-bit support + * on small targets, so don't define it if you don't need it. + */ +/* #define UNITY_INCLUDE_64 */ + + +/* *************************** FLOATING POINT TYPES **************************** + * In the embedded world, it's not uncommon for targets to have no support for + * floating point operations at all or to have support that is limited to only + * single precision. We are able to guess integer sizes on the fly because + * integers are always available in at least one size. Floating point, on the + * other hand, is sometimes not available at all. Trying to include `float.h` on + * these platforms would result in an error. This leaves manual configuration as + * the only option. + **************************************************************************** */ + + /* By default, Unity guesses that you will want single precision floating point + * support, but not double precision. It's easy to change either of these using + * the include and exclude options here. You may include neither, just float, + * or both, as suits your needs. + */ +/* #define UNITY_EXCLUDE_FLOAT */ +/* #define UNITY_INCLUDE_DOUBLE */ +/* #define UNITY_EXCLUDE_DOUBLE */ + +/* For features that are enabled, the following floating point options also + * become available. + */ + +/* Unity aims for as small of a footprint as possible and avoids most standard + * library calls (some embedded platforms don't have a standard library!). + * Because of this, its routines for printing integer values are minimalist and + * hand-coded. To keep Unity universal, though, we eventually chose to develop + * our own floating point print routines. Still, the display of floating point + * values during a failure are optional. By default, Unity will print the + * actual results of floating point assertion failures. So a failed assertion + * will produce a message like "Expected 4.0 Was 4.25". If you would like less + * verbose failure messages for floating point assertions, use this option to + * give a failure message `"Values Not Within Delta"` and trim the binary size. + */ +/* #define UNITY_EXCLUDE_FLOAT_PRINT */ + +/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C + * floats. If your compiler supports a specialty floating point type, you can + * always override this behavior by using this definition. + * + * Example: + */ +/* #define UNITY_FLOAT_TYPE float16_t */ + +/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard + * C doubles. If you would like to change this, you can specify something else + * by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long + * double` could enable gargantuan floating point types on your 64-bit processor + * instead of the standard `double`. + * + * Example: + */ +/* #define UNITY_DOUBLE_TYPE long double */ + +/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as + * documented in the Unity Assertion Guide, you will learn that they are not + * really asserting that two values are equal but rather that two values are + * "close enough" to equal. "Close enough" is controlled by these precision + * configuration options. If you are working with 32-bit floats and/or 64-bit + * doubles (the normal on most processors), you should have no need to change + * these options. They are both set to give you approximately 1 significant bit + * in either direction. The float precision is 0.00001 while the double is + * 10^-12. For further details on how this works, see the appendix of the Unity + * Assertion Guide. + * + * Example: + */ +/* #define UNITY_FLOAT_PRECISION 0.001f */ +/* #define UNITY_DOUBLE_PRECISION 0.001f */ + + +/* *************************** MISCELLANEOUS *********************************** + * Miscellaneous configuration options for Unity + **************************************************************************** */ + +/* Unity uses the stddef.h header included in the C standard library for the + * "NULL" macro. Define this in order to disable the include of stddef.h. If you + * do this, you have to make sure to provide your own "NULL" definition. + */ +/* #define UNITY_EXCLUDE_STDDEF_H */ + +/* Define this to enable the unity formatted print macro: + * "TEST_PRINTF" + */ +/* #define UNITY_INCLUDE_PRINT_FORMATTED */ + + +/* *************************** TOOLSET CUSTOMIZATION *************************** + * In addition to the options listed above, there are a number of other options + * which will come in handy to customize Unity's behavior for your specific + * toolchain. It is possible that you may not need to touch any of these but + * certain platforms, particularly those running in simulators, may need to jump + * through extra hoops to operate properly. These macros will help in those + * situations. + **************************************************************************** */ + +/* By default, Unity prints its results to `stdout` as it runs. This works + * perfectly fine in most situations where you are using a native compiler for + * testing. It works on some simulators as well so long as they have `stdout` + * routed back to the command line. There are times, however, where the + * simulator will lack support for dumping results or you will want to route + * results elsewhere for other reasons. In these cases, you should define the + * `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time + * (as an `int`, since this is the parameter type of the standard C `putchar` + * function most commonly used). You may replace this with whatever function + * call you like. + * + * Example: + * Say you are forced to run your test suite on an embedded processor with no + * `stdout` option. You decide to route your test result output to a custom + * serial `RS232_putc()` function you wrote like thus: + */ +/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ +/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ +/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ +/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ +/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ +/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ + +/* Some compilers require a custom attribute to be assigned to pointers, like + * `near` or `far`. In these cases, you can give Unity a safe default for these + * by defining this option with the attribute you would like. + * + * Example: + */ +/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */ +/* #define UNITY_PTR_ATTRIBUTE near */ + +/* Print execution time of each test when executed in verbose mode + * + * Example: + * + * TEST - PASS (10 ms) + */ +/* #define UNITY_INCLUDE_EXEC_TIME */ + +#endif /* UNITY_CONFIG_H */ \ No newline at end of file From 0f8ea3188e40121fa916b690a728e45b75cd8c67 Mon Sep 17 00:00:00 2001 From: Pieter Geelen Date: Fri, 29 Dec 2023 16:37:23 +0100 Subject: [PATCH 07/21] Dependency Unity for testing should not be part of build dependencies, unity_config.h can be removed and Test items are correctly referenced. --- controller/tea_poor/platformio.ini | 1 - .../test_local/WaterPumpScheduler_test.cpp | 12 +- .../tea_poor/test/test_local/unity_config.h | 244 ------------------ 3 files changed, 6 insertions(+), 251 deletions(-) delete mode 100644 controller/tea_poor/test/test_local/unity_config.h diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index e711024..a4b4776 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -15,6 +15,5 @@ framework = arduino test_framework = unity lib_deps = lasselukkari/aWOT@^3.5.0 - throwtheswitch/Unity@^2.5.2 test_ignore = local diff --git a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp index dc43712..3417fe9 100644 --- a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp @@ -37,17 +37,17 @@ void test_pump_stops_after_given_time() { waterPumpScheduler.start(runTimeMs, currentTimeMs); // check status auto status = waterPumpScheduler.status(); - UNITY_TEST_ASSERT_TRUE(status.isRunning); - UNITY_TEST_ASSERT_EQUAL(status.stopTime, runTimeMs); + TEST_ASSERT_TRUE(status.isRunning); + TEST_ASSERT_EQUAL(status.stopTime, runTimeMs); while (currentTimeMs < runTimeMs) { waterPumpScheduler.tick(currentTimeMs); - UNITY_TEST_ASSERT_TRUE(fakeWaterPump.isRunning()); + TEST_ASSERT_TRUE(fakeWaterPump.isRunning()); currentTimeMs += 100; } // pump should be stopped after given time waterPumpScheduler.tick(runTimeMs + 1); - UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); + TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); } // test that pump is periodically forced to stop after given time @@ -60,14 +60,14 @@ void test_pump_is_periodically_forced_to_stop_after_given_time () { waterPumpScheduler.start(1, currentTimeMs); currentTimeMs += 1; waterPumpScheduler.tick(currentTimeMs); - UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time + TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time for(int i = 0; i < 10; i++) { // emulate that pump was started again fakeWaterPump.start(); currentTimeMs += 1000; waterPumpScheduler.tick(currentTimeMs); - UNITY_TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped + TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped } } diff --git a/controller/tea_poor/test/test_local/unity_config.h b/controller/tea_poor/test/test_local/unity_config.h deleted file mode 100644 index f45e802..0000000 --- a/controller/tea_poor/test/test_local/unity_config.h +++ /dev/null @@ -1,244 +0,0 @@ -/* Unity Configuration - * As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529 - * Update: December 29th, 2016 - * See Also: Unity/docs/UnityConfigurationGuide.pdf - * - * Unity is designed to run on almost anything that is targeted by a C compiler. - * It would be awesome if this could be done with zero configuration. While - * there are some targets that come close to this dream, it is sadly not - * universal. It is likely that you are going to need at least a couple of the - * configuration options described in this document. - * - * All of Unity's configuration options are `#defines`. Most of these are simple - * definitions. A couple are macros with arguments. They live inside the - * unity_internals.h header file. We don't necessarily recommend opening that - * file unless you really need to. That file is proof that a cross-platform - * library is challenging to build. From a more positive perspective, it is also - * proof that a great deal of complexity can be centralized primarily to one - * place in order to provide a more consistent and simple experience elsewhere. - * - * Using These Options - * It doesn't matter if you're using a target-specific compiler and a simulator - * or a native compiler. In either case, you've got a couple choices for - * configuring these options: - * - * 1. Because these options are specified via C defines, you can pass most of - * these options to your compiler through command line compiler flags. Even - * if you're using an embedded target that forces you to use their - * overbearing IDE for all configuration, there will be a place somewhere in - * your project to configure defines for your compiler. - * 2. You can create a custom `unity_config.h` configuration file (present in - * your toolchain's search paths). In this file, you will list definitions - * and macros specific to your target. All you must do is define - * `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any - * further definitions it may need. - */ - -#ifndef UNITY_CONFIG_H -#define UNITY_CONFIG_H - -/* ************************* AUTOMATIC INTEGER TYPES *************************** - * C's concept of an integer varies from target to target. The C Standard has - * rules about the `int` matching the register size of the target - * microprocessor. It has rules about the `int` and how its size relates to - * other integer types. An `int` on one target might be 16 bits while on another - * target it might be 64. There are more specific types in compilers compliant - * with C99 or later, but that's certainly not every compiler you are likely to - * encounter. Therefore, Unity has a number of features for helping to adjust - * itself to match your required integer sizes. It starts off by trying to do it - * automatically. - **************************************************************************** */ - -/* The first attempt to guess your types is to check `limits.h`. Some compilers - * that don't support `stdint.h` could include `limits.h`. If you don't - * want Unity to check this file, define this to make it skip the inclusion. - * Unity looks at UINT_MAX & ULONG_MAX, which were available since C89. - */ -/* #define UNITY_EXCLUDE_LIMITS_H */ - -/* The second thing that Unity does to guess your types is check `stdint.h`. - * This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to - * learn about your system. It's possible you don't want it to do this or it's - * possible that your system doesn't support `stdint.h`. If that's the case, - * you're going to want to define this. That way, Unity will know to skip the - * inclusion of this file and you won't be left with a compiler error. - */ -/* #define UNITY_EXCLUDE_STDINT_H */ - -/* ********************** MANUAL INTEGER TYPE DEFINITION *********************** - * If you've disabled all of the automatic options above, you're going to have - * to do the configuration yourself. There are just a handful of defines that - * you are going to specify if you don't like the defaults. - **************************************************************************** */ - - /* Define this to be the number of bits an `int` takes up on your system. The - * default, if not auto-detected, is 32 bits. - * - * Example: - */ -/* #define UNITY_INT_WIDTH 16 */ - -/* Define this to be the number of bits a `long` takes up on your system. The - * default, if not autodetected, is 32 bits. This is used to figure out what - * kind of 64-bit support your system can handle. Does it need to specify a - * `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option - * is going to be ignored. - * - * Example: - */ -/* #define UNITY_LONG_WIDTH 16 */ - -/* Define this to be the number of bits a pointer takes up on your system. The - * default, if not autodetected, is 32-bits. If you're getting ugly compiler - * warnings about casting from pointers, this is the one to look at. - * - * Example: - */ -/* #define UNITY_POINTER_WIDTH 64 */ - -/* Unity will automatically include 64-bit support if it auto-detects it, or if - * your `int`, `long`, or pointer widths are greater than 32-bits. Define this - * to enable 64-bit support if none of the other options already did it for you. - * There can be a significant size and speed impact to enabling 64-bit support - * on small targets, so don't define it if you don't need it. - */ -/* #define UNITY_INCLUDE_64 */ - - -/* *************************** FLOATING POINT TYPES **************************** - * In the embedded world, it's not uncommon for targets to have no support for - * floating point operations at all or to have support that is limited to only - * single precision. We are able to guess integer sizes on the fly because - * integers are always available in at least one size. Floating point, on the - * other hand, is sometimes not available at all. Trying to include `float.h` on - * these platforms would result in an error. This leaves manual configuration as - * the only option. - **************************************************************************** */ - - /* By default, Unity guesses that you will want single precision floating point - * support, but not double precision. It's easy to change either of these using - * the include and exclude options here. You may include neither, just float, - * or both, as suits your needs. - */ -/* #define UNITY_EXCLUDE_FLOAT */ -/* #define UNITY_INCLUDE_DOUBLE */ -/* #define UNITY_EXCLUDE_DOUBLE */ - -/* For features that are enabled, the following floating point options also - * become available. - */ - -/* Unity aims for as small of a footprint as possible and avoids most standard - * library calls (some embedded platforms don't have a standard library!). - * Because of this, its routines for printing integer values are minimalist and - * hand-coded. To keep Unity universal, though, we eventually chose to develop - * our own floating point print routines. Still, the display of floating point - * values during a failure are optional. By default, Unity will print the - * actual results of floating point assertion failures. So a failed assertion - * will produce a message like "Expected 4.0 Was 4.25". If you would like less - * verbose failure messages for floating point assertions, use this option to - * give a failure message `"Values Not Within Delta"` and trim the binary size. - */ -/* #define UNITY_EXCLUDE_FLOAT_PRINT */ - -/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C - * floats. If your compiler supports a specialty floating point type, you can - * always override this behavior by using this definition. - * - * Example: - */ -/* #define UNITY_FLOAT_TYPE float16_t */ - -/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard - * C doubles. If you would like to change this, you can specify something else - * by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long - * double` could enable gargantuan floating point types on your 64-bit processor - * instead of the standard `double`. - * - * Example: - */ -/* #define UNITY_DOUBLE_TYPE long double */ - -/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as - * documented in the Unity Assertion Guide, you will learn that they are not - * really asserting that two values are equal but rather that two values are - * "close enough" to equal. "Close enough" is controlled by these precision - * configuration options. If you are working with 32-bit floats and/or 64-bit - * doubles (the normal on most processors), you should have no need to change - * these options. They are both set to give you approximately 1 significant bit - * in either direction. The float precision is 0.00001 while the double is - * 10^-12. For further details on how this works, see the appendix of the Unity - * Assertion Guide. - * - * Example: - */ -/* #define UNITY_FLOAT_PRECISION 0.001f */ -/* #define UNITY_DOUBLE_PRECISION 0.001f */ - - -/* *************************** MISCELLANEOUS *********************************** - * Miscellaneous configuration options for Unity - **************************************************************************** */ - -/* Unity uses the stddef.h header included in the C standard library for the - * "NULL" macro. Define this in order to disable the include of stddef.h. If you - * do this, you have to make sure to provide your own "NULL" definition. - */ -/* #define UNITY_EXCLUDE_STDDEF_H */ - -/* Define this to enable the unity formatted print macro: - * "TEST_PRINTF" - */ -/* #define UNITY_INCLUDE_PRINT_FORMATTED */ - - -/* *************************** TOOLSET CUSTOMIZATION *************************** - * In addition to the options listed above, there are a number of other options - * which will come in handy to customize Unity's behavior for your specific - * toolchain. It is possible that you may not need to touch any of these but - * certain platforms, particularly those running in simulators, may need to jump - * through extra hoops to operate properly. These macros will help in those - * situations. - **************************************************************************** */ - -/* By default, Unity prints its results to `stdout` as it runs. This works - * perfectly fine in most situations where you are using a native compiler for - * testing. It works on some simulators as well so long as they have `stdout` - * routed back to the command line. There are times, however, where the - * simulator will lack support for dumping results or you will want to route - * results elsewhere for other reasons. In these cases, you should define the - * `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time - * (as an `int`, since this is the parameter type of the standard C `putchar` - * function most commonly used). You may replace this with whatever function - * call you like. - * - * Example: - * Say you are forced to run your test suite on an embedded processor with no - * `stdout` option. You decide to route your test result output to a custom - * serial `RS232_putc()` function you wrote like thus: - */ -/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ -/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ -/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ -/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ -/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ -/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ - -/* Some compilers require a custom attribute to be assigned to pointers, like - * `near` or `far`. In these cases, you can give Unity a safe default for these - * by defining this option with the attribute you would like. - * - * Example: - */ -/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */ -/* #define UNITY_PTR_ATTRIBUTE near */ - -/* Print execution time of each test when executed in verbose mode - * - * Example: - * - * TEST - PASS (10 ms) - */ -/* #define UNITY_INCLUDE_EXEC_TIME */ - -#endif /* UNITY_CONFIG_H */ \ No newline at end of file From 5e0bbcfc763343d26da55035c6ad7b637feaa0b8 Mon Sep 17 00:00:00 2001 From: omonrise Date: Fri, 29 Dec 2023 16:44:48 +0100 Subject: [PATCH 08/21] remove unity from libdeps --- controller/tea_poor/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index e711024..a8fe5b5 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -15,6 +15,6 @@ framework = arduino test_framework = unity lib_deps = lasselukkari/aWOT@^3.5.0 - throwtheswitch/Unity@^2.5.2 + ; throwtheswitch/Unity@^2.5.2 test_ignore = local From 3288759c6b5f16ea73c2b572e5465a31670207ef Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 29 Dec 2023 22:08:46 +0100 Subject: [PATCH 09/21] fix everything? --- .../lib/WaterPump/WaterPumpScheduler.cpp | 10 ++++- .../WaterPumpController.cpp | 0 .../WaterPumpController.h | 2 +- controller/tea_poor/platformio.ini | 20 +++++++-- .../WaterPumpScheduler_test.cpp | 42 +++++++------------ 5 files changed, 41 insertions(+), 33 deletions(-) rename controller/tea_poor/lib/{WaterPump => WaterPumpController}/WaterPumpController.cpp (100%) rename controller/tea_poor/lib/{WaterPump => WaterPumpController}/WaterPumpController.h (95%) rename controller/tea_poor/test/{test_local => test_native}/WaterPumpScheduler_test.cpp (69%) diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp index 21bb9fe..888b4bf 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp @@ -1,10 +1,16 @@ #include "WaterPumpScheduler.h" -WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) { +WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) : + _waterPump(waterPump), + _forceStopIntervalMs(forceStopIntervalMs) +{ } WaterPumpScheduler::~WaterPumpScheduler() { - delete _waterPump; + // TODO: find better way to manage memory + // for now it's not a big deal, because Arduino will never stop + // and tests are manage memory by themselves + // delete _waterPump; } void WaterPumpScheduler::setup() { diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpController.cpp b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp similarity index 100% rename from controller/tea_poor/lib/WaterPump/WaterPumpController.cpp rename to controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpController.h b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h similarity index 95% rename from controller/tea_poor/lib/WaterPump/WaterPumpController.h rename to controller/tea_poor/lib/WaterPumpController/WaterPumpController.h index 511560c..2e55cac 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpController.h +++ b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h @@ -1,6 +1,6 @@ #ifndef WATERPUMPCONTROLLER_H #define WATERPUMPCONTROLLER_H -#include "IWaterPump.h" +#include class WaterPumpController: public IWaterPump { private: diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index a8fe5b5..10b8810 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -12,9 +12,23 @@ platform = renesas-ra board = uno_r4_wifi framework = arduino -test_framework = unity lib_deps = lasselukkari/aWOT@^3.5.0 - ; throwtheswitch/Unity@^2.5.2 -test_ignore = local +test_ignore = test_native +[env:native] +platform = native +test_build_src = no +test_framework = googletest +build_src_filter = +<*> - +lib_ldf_mode = deep +check_flags = --verbose --enable=all --std=c++11 +build_flags = + -std=c++11 + -Wall -Wextra -Wunused + -static -static-libgcc -static-libstdc++ +; ignore libraries that are only for the Arduino +lib_ignore = + RemoteControl + WaterPumpController +test_ignore = test_uno_r4_wifi \ No newline at end of file diff --git a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp similarity index 69% rename from controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp rename to controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp index 3417fe9..8e2cd96 100644 --- a/controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp @@ -1,11 +1,9 @@ // I wasn't able to run tests at all. Run them locally and confirm that they are working. // Its either a local problem or a problem with the configuration of the project. // Further goes a sketch of the tests, but I wasn't able to run them. -#include +#include #include - - // Fake water pump class FakeWaterPump : public IWaterPump { private: @@ -19,14 +17,8 @@ class FakeWaterPump : public IWaterPump { }; // End of fake water pump - -/* Empty functions required by unity framework*/ -void setUp() { /* Setup code here */ } -void tearDown() { /* Teardown code here */ } - - // test that pump is stopping after given time -void test_pump_stops_after_given_time() { +TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { // random time between 1 and 10 seconds const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000; FakeWaterPump fakeWaterPump; @@ -37,21 +29,21 @@ void test_pump_stops_after_given_time() { waterPumpScheduler.start(runTimeMs, currentTimeMs); // check status auto status = waterPumpScheduler.status(); - TEST_ASSERT_TRUE(status.isRunning); - TEST_ASSERT_EQUAL(status.stopTime, runTimeMs); + ASSERT_TRUE(status.isRunning); + ASSERT_EQ(status.stopTime, runTimeMs); while (currentTimeMs < runTimeMs) { waterPumpScheduler.tick(currentTimeMs); - TEST_ASSERT_TRUE(fakeWaterPump.isRunning()); + ASSERT_TRUE(fakeWaterPump.isRunning()); currentTimeMs += 100; } // pump should be stopped after given time waterPumpScheduler.tick(runTimeMs + 1); - TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); + ASSERT_FALSE(fakeWaterPump.isRunning()); } // test that pump is periodically forced to stop after given time -void test_pump_is_periodically_forced_to_stop_after_given_time () { +TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) { FakeWaterPump fakeWaterPump; WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second waterPumpScheduler.setup(); @@ -60,25 +52,21 @@ void test_pump_is_periodically_forced_to_stop_after_given_time () { waterPumpScheduler.start(1, currentTimeMs); currentTimeMs += 1; waterPumpScheduler.tick(currentTimeMs); - TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time + ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time for(int i = 0; i < 10; i++) { // emulate that pump was started again fakeWaterPump.start(); currentTimeMs += 1000; waterPumpScheduler.tick(currentTimeMs); - TEST_ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped + ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped } } -void setup() { - UNITY_BEGIN(); - RUN_TEST(test_pump_stops_after_given_time); - RUN_TEST(test_pump_is_periodically_forced_to_stop_after_given_time); - UNITY_END(); -} - - -void loop() { - +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); // Intentionally ignoring the return value + (void)result; // Silence unused variable warning + // Always return zero-code and allow PlatformIO to parse results + return 0; } \ No newline at end of file From 7f01633df597588086b6cce83b891b2fef5f86ad Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 29 Dec 2023 22:12:59 +0100 Subject: [PATCH 10/21] default env for platformio build --- controller/tea_poor/platformio.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 10b8810..82d27b9 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -31,4 +31,7 @@ build_flags = lib_ignore = RemoteControl WaterPumpController -test_ignore = test_uno_r4_wifi \ No newline at end of file +test_ignore = test_uno_r4_wifi + +[platformio] +default_envs = uno_r4_wifi \ No newline at end of file From 6dd04631ce8024be5069e9096700bd76b4b87cc3 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 29 Dec 2023 22:21:19 +0100 Subject: [PATCH 11/21] configure CI to build only for Android --- .github/workflows/platformio.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platformio.yaml b/.github/workflows/platformio.yaml index 460fb89..90a47b5 100644 --- a/.github/workflows/platformio.yaml +++ b/.github/workflows/platformio.yaml @@ -20,6 +20,9 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - - name: Build PlatformIO Project + - name: Run tests on the native platform + run: platformio test -e native + + - name: Build PlatformIO Project for Android working-directory: ./controller/tea_poor - run: pio run + run: pio run -e uno_r4_wifi From 7c12ef04bf26bc0c75309274b86937b76ba0cc05 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 29 Dec 2023 22:26:24 +0100 Subject: [PATCH 12/21] fix --- .github/workflows/platformio.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/platformio.yaml b/.github/workflows/platformio.yaml index 90a47b5..c24ab99 100644 --- a/.github/workflows/platformio.yaml +++ b/.github/workflows/platformio.yaml @@ -21,7 +21,8 @@ jobs: run: pip install --upgrade platformio - name: Run tests on the native platform - run: platformio test -e native + working-directory: ./controller/tea_poor + run: pio test -e native - name: Build PlatformIO Project for Android working-directory: ./controller/tea_poor From e2f62b68d500e351fb55ab61e1d50c8e7d2165fb Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 29 Dec 2023 22:49:41 +0100 Subject: [PATCH 13/21] fix typo --- .github/workflows/platformio.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platformio.yaml b/.github/workflows/platformio.yaml index c24ab99..95b86ce 100644 --- a/.github/workflows/platformio.yaml +++ b/.github/workflows/platformio.yaml @@ -24,6 +24,6 @@ jobs: working-directory: ./controller/tea_poor run: pio test -e native - - name: Build PlatformIO Project for Android + - name: Build PlatformIO Project for Arduino working-directory: ./controller/tea_poor run: pio run -e uno_r4_wifi From 7761f469e8f3c760df8c9c7ef54828e025f71756 Mon Sep 17 00:00:00 2001 From: Pieter Geelen Date: Fri, 29 Dec 2023 23:01:14 +0100 Subject: [PATCH 14/21] Separate out CI --- ...{platformio.yaml => platformio_build.yaml} | 6 +---- .github/workflows/platformio_tests.yaml | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) rename .github/workflows/{platformio.yaml => platformio_build.yaml} (79%) create mode 100644 .github/workflows/platformio_tests.yaml diff --git a/.github/workflows/platformio.yaml b/.github/workflows/platformio_build.yaml similarity index 79% rename from .github/workflows/platformio.yaml rename to .github/workflows/platformio_build.yaml index 95b86ce..29ddd58 100644 --- a/.github/workflows/platformio.yaml +++ b/.github/workflows/platformio_build.yaml @@ -1,4 +1,4 @@ -name: PlatformIO CI +name: PlatformIO CI - Build for Arduino on: [push] @@ -20,10 +20,6 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - - name: Run tests on the native platform - working-directory: ./controller/tea_poor - run: pio test -e native - - name: Build PlatformIO Project for Arduino working-directory: ./controller/tea_poor run: pio run -e uno_r4_wifi diff --git a/.github/workflows/platformio_tests.yaml b/.github/workflows/platformio_tests.yaml new file mode 100644 index 0000000..299db5f --- /dev/null +++ b/.github/workflows/platformio_tests.yaml @@ -0,0 +1,25 @@ +name: PlatformIO CI - Unit Tests + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v4 + with: + python-version: "3.9" + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Run tests on the native platform + working-directory: ./controller/tea_poor + run: pio test -e native From 7046cf40d8e3c171ea9575a03f4ece5f024b7034 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 10:16:07 +0100 Subject: [PATCH 15/21] Update user authentication logic --- controller/tea_poor/lib/WaterPump/IWaterPump.h | 4 ++++ .../lib/WaterPump/WaterPumpScheduler.cpp | 9 ++------- .../lib/WaterPump/WaterPumpScheduler.h | 6 +++--- controller/tea_poor/src/main.cpp | 8 ++++---- .../test_native/WaterPumpScheduler_test.cpp | 18 +++++++++--------- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/controller/tea_poor/lib/WaterPump/IWaterPump.h b/controller/tea_poor/lib/WaterPump/IWaterPump.h index 4377980..eef9ee9 100644 --- a/controller/tea_poor/lib/WaterPump/IWaterPump.h +++ b/controller/tea_poor/lib/WaterPump/IWaterPump.h @@ -1,6 +1,8 @@ #ifndef IWATERPUMP_H #define IWATERPUMP_H +#include + class IWaterPump { public: virtual ~IWaterPump() {} @@ -12,4 +14,6 @@ class IWaterPump { virtual bool isRunning() const = 0; }; +// define shared pointer alias +using IWaterPumpPtr = std::shared_ptr; #endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp index 888b4bf..eb9ae02 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp @@ -1,17 +1,12 @@ #include "WaterPumpScheduler.h" -WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) : +WaterPumpScheduler::WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs) : _waterPump(waterPump), _forceStopIntervalMs(forceStopIntervalMs) { } -WaterPumpScheduler::~WaterPumpScheduler() { - // TODO: find better way to manage memory - // for now it's not a big deal, because Arduino will never stop - // and tests are manage memory by themselves - // delete _waterPump; -} +WaterPumpScheduler::~WaterPumpScheduler() {} void WaterPumpScheduler::setup() { _waterPump->setup(); diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h index 6abb21f..84a6145 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h @@ -8,13 +8,13 @@ // It is also ensuring that water pump is stopped if not needed class WaterPumpScheduler { private: - IWaterPump* _waterPump; + IWaterPumpPtr _waterPump; unsigned long _stopTime = 0; // each X milliseconds will force stop water pump unsigned long _forceStopIntervalMs; public: - WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs); - WaterPumpScheduler(IWaterPump* waterPump) : WaterPumpScheduler(waterPump, 1000) {} + WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs); + WaterPumpScheduler(IWaterPumpPtr waterPump) : WaterPumpScheduler(waterPump, 1000) {} ~WaterPumpScheduler(); void setup(); diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index 82747dd..0a25e02 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -1,11 +1,12 @@ #include +#include #include #include #include // Setting up water pump WaterPumpScheduler waterPump( - new WaterPumpController(12, 9, 3) + std::make_shared(12, 9, 3) ); // Just for safety reasons, we don't want to pour tea for too long // Their is no reason to make it configurable and add unnecessary complexity @@ -59,9 +60,8 @@ void pour_tea(Request &req, Response &res) { char milliseconds[64]; req.query("milliseconds", milliseconds, 64); if (!isValidIntNumber(milliseconds, WATER_PUMP_SAFE_THRESHOLD)) { - res.println("Please specify amount of milliseconds in query parameter; pour_tea?milliseconds=10 e.g."); - res.print("Maximal allowed time is: "); - res.println(WATER_PUMP_SAFE_THRESHOLD); + // send error message as JSON + res.println("{ \"error\": \"invalid milliseconds value\" }"); return; } // start pouring tea diff --git a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp index 8e2cd96..2b3b40f 100644 --- a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp @@ -21,8 +21,8 @@ class FakeWaterPump : public IWaterPump { TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { // random time between 1 and 10 seconds const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000; - FakeWaterPump fakeWaterPump; - WaterPumpScheduler waterPumpScheduler(&fakeWaterPump); + IWaterPumpPtr fakeWaterPump = std::make_shared(); + WaterPumpScheduler waterPumpScheduler(fakeWaterPump); waterPumpScheduler.setup(); // start water pump unsigned long currentTimeMs = 0; @@ -34,32 +34,32 @@ TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { while (currentTimeMs < runTimeMs) { waterPumpScheduler.tick(currentTimeMs); - ASSERT_TRUE(fakeWaterPump.isRunning()); + ASSERT_TRUE(fakeWaterPump->isRunning()); currentTimeMs += 100; } // pump should be stopped after given time waterPumpScheduler.tick(runTimeMs + 1); - ASSERT_FALSE(fakeWaterPump.isRunning()); + ASSERT_FALSE(fakeWaterPump->isRunning()); } // test that pump is periodically forced to stop after given time TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) { - FakeWaterPump fakeWaterPump; - WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second + IWaterPumpPtr fakeWaterPump = std::make_shared(); + WaterPumpScheduler waterPumpScheduler(fakeWaterPump, 1000); // force stop each 1 second waterPumpScheduler.setup(); // start water pump unsigned long currentTimeMs = 0; waterPumpScheduler.start(1, currentTimeMs); currentTimeMs += 1; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped after given time for(int i = 0; i < 10; i++) { // emulate that pump was started again - fakeWaterPump.start(); + fakeWaterPump->start(); currentTimeMs += 1000; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped } } From 89347f0e5ea88ebe97624073d5dee445a162c77e Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 10:16:07 +0100 Subject: [PATCH 16/21] shared_ptr + return error as JSON --- controller/tea_poor/lib/WaterPump/IWaterPump.h | 4 ++++ .../lib/WaterPump/WaterPumpScheduler.cpp | 9 ++------- .../lib/WaterPump/WaterPumpScheduler.h | 6 +++--- controller/tea_poor/src/main.cpp | 8 ++++---- .../test_native/WaterPumpScheduler_test.cpp | 18 +++++++++--------- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/controller/tea_poor/lib/WaterPump/IWaterPump.h b/controller/tea_poor/lib/WaterPump/IWaterPump.h index 4377980..eef9ee9 100644 --- a/controller/tea_poor/lib/WaterPump/IWaterPump.h +++ b/controller/tea_poor/lib/WaterPump/IWaterPump.h @@ -1,6 +1,8 @@ #ifndef IWATERPUMP_H #define IWATERPUMP_H +#include + class IWaterPump { public: virtual ~IWaterPump() {} @@ -12,4 +14,6 @@ class IWaterPump { virtual bool isRunning() const = 0; }; +// define shared pointer alias +using IWaterPumpPtr = std::shared_ptr; #endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp index 888b4bf..eb9ae02 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp @@ -1,17 +1,12 @@ #include "WaterPumpScheduler.h" -WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) : +WaterPumpScheduler::WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs) : _waterPump(waterPump), _forceStopIntervalMs(forceStopIntervalMs) { } -WaterPumpScheduler::~WaterPumpScheduler() { - // TODO: find better way to manage memory - // for now it's not a big deal, because Arduino will never stop - // and tests are manage memory by themselves - // delete _waterPump; -} +WaterPumpScheduler::~WaterPumpScheduler() {} void WaterPumpScheduler::setup() { _waterPump->setup(); diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h index 6abb21f..84a6145 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h +++ b/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h @@ -8,13 +8,13 @@ // It is also ensuring that water pump is stopped if not needed class WaterPumpScheduler { private: - IWaterPump* _waterPump; + IWaterPumpPtr _waterPump; unsigned long _stopTime = 0; // each X milliseconds will force stop water pump unsigned long _forceStopIntervalMs; public: - WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs); - WaterPumpScheduler(IWaterPump* waterPump) : WaterPumpScheduler(waterPump, 1000) {} + WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs); + WaterPumpScheduler(IWaterPumpPtr waterPump) : WaterPumpScheduler(waterPump, 1000) {} ~WaterPumpScheduler(); void setup(); diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index 82747dd..0a25e02 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -1,11 +1,12 @@ #include +#include #include #include #include // Setting up water pump WaterPumpScheduler waterPump( - new WaterPumpController(12, 9, 3) + std::make_shared(12, 9, 3) ); // Just for safety reasons, we don't want to pour tea for too long // Their is no reason to make it configurable and add unnecessary complexity @@ -59,9 +60,8 @@ void pour_tea(Request &req, Response &res) { char milliseconds[64]; req.query("milliseconds", milliseconds, 64); if (!isValidIntNumber(milliseconds, WATER_PUMP_SAFE_THRESHOLD)) { - res.println("Please specify amount of milliseconds in query parameter; pour_tea?milliseconds=10 e.g."); - res.print("Maximal allowed time is: "); - res.println(WATER_PUMP_SAFE_THRESHOLD); + // send error message as JSON + res.println("{ \"error\": \"invalid milliseconds value\" }"); return; } // start pouring tea diff --git a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp index 8e2cd96..2b3b40f 100644 --- a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp @@ -21,8 +21,8 @@ class FakeWaterPump : public IWaterPump { TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { // random time between 1 and 10 seconds const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000; - FakeWaterPump fakeWaterPump; - WaterPumpScheduler waterPumpScheduler(&fakeWaterPump); + IWaterPumpPtr fakeWaterPump = std::make_shared(); + WaterPumpScheduler waterPumpScheduler(fakeWaterPump); waterPumpScheduler.setup(); // start water pump unsigned long currentTimeMs = 0; @@ -34,32 +34,32 @@ TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { while (currentTimeMs < runTimeMs) { waterPumpScheduler.tick(currentTimeMs); - ASSERT_TRUE(fakeWaterPump.isRunning()); + ASSERT_TRUE(fakeWaterPump->isRunning()); currentTimeMs += 100; } // pump should be stopped after given time waterPumpScheduler.tick(runTimeMs + 1); - ASSERT_FALSE(fakeWaterPump.isRunning()); + ASSERT_FALSE(fakeWaterPump->isRunning()); } // test that pump is periodically forced to stop after given time TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) { - FakeWaterPump fakeWaterPump; - WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second + IWaterPumpPtr fakeWaterPump = std::make_shared(); + WaterPumpScheduler waterPumpScheduler(fakeWaterPump, 1000); // force stop each 1 second waterPumpScheduler.setup(); // start water pump unsigned long currentTimeMs = 0; waterPumpScheduler.start(1, currentTimeMs); currentTimeMs += 1; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped after given time + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped after given time for(int i = 0; i < 10; i++) { // emulate that pump was started again - fakeWaterPump.start(); + fakeWaterPump->start(); currentTimeMs += 1000; waterPumpScheduler.tick(currentTimeMs); - ASSERT_FALSE(fakeWaterPump.isRunning()); // pump should be stopped + ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped } } From 63bee21db17aaf5c7e07bb45ee765dd09a20947b Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 12:50:17 +0100 Subject: [PATCH 17/21] small rework --- controller/tea_poor/src/main.cpp | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index 0a25e02..adfa85b 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -4,6 +4,7 @@ #include #include +#include // Setting up water pump WaterPumpScheduler waterPump( std::make_shared(12, 9, 3) @@ -18,34 +19,27 @@ RemoteControl remoteControl( "VerySecurePassword" // network password ); -void _sendSystemStatus(Response &res) { - // send system status as JSON - res.println("{"); +void _sendSystemStatus(std::ostream& response) { + response << "{"; // send water threshold - res.print("\"water threshold\": "); - res.print(WATER_PUMP_SAFE_THRESHOLD); - res.println(","); - + response << "\"water threshold\": " << WATER_PUMP_SAFE_THRESHOLD << ","; // send water pump status const auto waterPumpStatus = waterPump.status(); - res.println("\"pump\": {"); - res.print("\"running\": "); - res.print(waterPumpStatus.isRunning ? "true, " : "false, "); const unsigned long timeLeft = waterPumpStatus.isRunning ? waterPumpStatus.stopTime - millis() : 0; - res.print("\"time left\": "); - res.print(timeLeft); - res.println("},"); + response + << "\"pump\": {" + << " \"running\": " << (waterPumpStatus.isRunning ? "true, " : "false, ") + << " \"time left\": " << timeLeft + << "}"; // end of water pump status /////////////////////////////////// // send remote control status - res.print("\"remote control\": "); - res.print(remoteControl.asJSONString()); - res.println(); + response << "\"remote control\": " << remoteControl.asJSONString(); // end of JSON - res.println("}"); + response << "}"; } bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) { @@ -56,12 +50,10 @@ bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) return true; } -void pour_tea(Request &req, Response &res) { - char milliseconds[64]; - req.query("milliseconds", milliseconds, 64); +void pour_tea(const char *milliseconds, std::ostream &res) { if (!isValidIntNumber(milliseconds, WATER_PUMP_SAFE_THRESHOLD)) { // send error message as JSON - res.println("{ \"error\": \"invalid milliseconds value\" }"); + res << "{ \"error\": \"invalid milliseconds value\" }"; return; } // start pouring tea @@ -72,16 +64,28 @@ void pour_tea(Request &req, Response &res) { void setup() { Serial.begin(9600); waterPump.setup(); + // TODO: find a way to remove redundant code with string streams remoteControl.setup([](Application &app) { - app.get("/pour_tea", pour_tea); + app.get("/pour_tea", [](Request &req, Response &res) { + char milliseconds[64]; + req.query("milliseconds", milliseconds, 64); + + std::stringstream response; + pour_tea(milliseconds, response); + res.println(response.str().c_str()); + }); // stop water pump app.get("/stop", [](Request &req, Response &res) { waterPump.stop(); - _sendSystemStatus(res); + std::stringstream response; + _sendSystemStatus(response); + res.println(response.str().c_str()); }); // get system status app.get("/status", [](Request &req, Response &res) { - _sendSystemStatus(res); + std::stringstream response; + _sendSystemStatus(response); + res.println(response.str().c_str()); }); }); } From 311e31468aa251faf572ab696d15e7d2cabdbf6f Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 13:45:24 +0100 Subject: [PATCH 18/21] extracted secrets from main.cpp --- controller/tea_poor/.gitignore | 3 +++ controller/tea_poor/src/main.cpp | 10 +++++----- controller/tea_poor/src/secrets.h.example | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 controller/tea_poor/src/secrets.h.example diff --git a/controller/tea_poor/.gitignore b/controller/tea_poor/.gitignore index 89cc49c..1f7aa88 100644 --- a/controller/tea_poor/.gitignore +++ b/controller/tea_poor/.gitignore @@ -3,3 +3,6 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch + +# hide secrets +src/secrets.h \ No newline at end of file diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index adfa85b..b439390 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -3,21 +3,21 @@ #include #include #include +#include "secrets.h" #include // Setting up water pump WaterPumpScheduler waterPump( - std::make_shared(12, 9, 3) + std::make_shared( + WATER_PUMP_DIRECTION_PIN, WATER_PUMP_BRAKE_PIN, WATER_PUMP_POWER_PIN + ) ); // Just for safety reasons, we don't want to pour tea for too long // Their is no reason to make it configurable and add unnecessary complexity const int WATER_PUMP_SAFE_THRESHOLD = 10 * 1000; // setting up remote control -RemoteControl remoteControl( - "MyWiFiNetwork", // network name/SSID - "VerySecurePassword" // network password -); +RemoteControl remoteControl(WIFI_SSID, WIFI_PASSWORD); void _sendSystemStatus(std::ostream& response) { response << "{"; diff --git a/controller/tea_poor/src/secrets.h.example b/controller/tea_poor/src/secrets.h.example new file mode 100644 index 0000000..84d12c9 --- /dev/null +++ b/controller/tea_poor/src/secrets.h.example @@ -0,0 +1,14 @@ +// contains user specific information that should not be shared +#ifndef SECRETS_H +#define SECRETS_H + +// WiFi network name/SSID +const char* WIFI_SSID = "MyWiFiNetwork"; +const char* WIFI_PASSWORD = "VerySecurePassword"; + +// PINs for water pump controller +const int WATER_PUMP_DIRECTION_PIN = 12; +const int WATER_PUMP_BRAKE_PIN = 9; +const int WATER_PUMP_POWER_PIN = 3; + +#endif // SECRETS_H \ No newline at end of file From 75a76291969264cb38f910e8b81aa7430e222c46 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 13:51:37 +0100 Subject: [PATCH 19/21] fix CI --- .github/workflows/platformio_build.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/platformio_build.yaml b/.github/workflows/platformio_build.yaml index 29ddd58..0786ae9 100644 --- a/.github/workflows/platformio_build.yaml +++ b/.github/workflows/platformio_build.yaml @@ -20,6 +20,10 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio + # rename src/secrets.h.example to src/secrets.h, to use a dummy values during build + - name: Copy secrets.h + run: cp ./controller/tea_poor/src/secrets.h.example ./controller/tea_poor/src/secrets.h + - name: Build PlatformIO Project for Arduino working-directory: ./controller/tea_poor run: pio run -e uno_r4_wifi From 267322c9ea9e7c5e4a7fe03da641ac7bcbf57156 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Sat, 30 Dec 2023 22:37:28 +0100 Subject: [PATCH 20/21] very big update. Reorganized the code, added a command processor, tests. --- .../tea_poor/lib/Arduino/ArduinoEnvironment.h | 15 ++++ .../RemoteControl.cpp | 0 .../RemoteControl.h | 0 .../WaterPumpController.cpp | 0 .../WaterPumpController.h | 0 .../lib/CommandProcessor/CommandProcessor.cpp | 58 ++++++++++++++ .../lib/CommandProcessor/CommandProcessor.h | 30 +++++++ .../WaterPumpScheduler.cpp | 7 +- .../WaterPumpScheduler.h | 17 ++-- .../tea_poor/lib/interfaces/IEnvironment.h | 13 +++ .../{WaterPump => interfaces}/IWaterPump.h | 0 .../lib/interfaces/IWaterPumpSchedulerAPI.h | 30 +++++++ controller/tea_poor/platformio.ini | 4 +- controller/tea_poor/src/main.cpp | 78 +++++------------- controller/tea_poor/src/secrets.h.example | 4 + controller/tea_poor/test/test_native/main.cpp | 13 +++ .../test_native/tests/CommandProcessor_test.h | 79 +++++++++++++++++++ .../WaterPumpScheduler_test.h} | 25 +----- .../test_native/tests/mocks/FakeEnvironment.h | 19 +++++ .../test_native/tests/mocks/FakeWaterPump.h | 18 +++++ .../tests/mocks/FakeWaterPumpSchedulerAPI.h | 26 ++++++ 21 files changed, 336 insertions(+), 100 deletions(-) create mode 100644 controller/tea_poor/lib/Arduino/ArduinoEnvironment.h rename controller/tea_poor/lib/{RemoteControl => Arduino}/RemoteControl.cpp (100%) rename controller/tea_poor/lib/{RemoteControl => Arduino}/RemoteControl.h (100%) rename controller/tea_poor/lib/{WaterPumpController => Arduino}/WaterPumpController.cpp (100%) rename controller/tea_poor/lib/{WaterPumpController => Arduino}/WaterPumpController.h (100%) create mode 100644 controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp create mode 100644 controller/tea_poor/lib/CommandProcessor/CommandProcessor.h rename controller/tea_poor/lib/{WaterPump => WaterPumpScheduler}/WaterPumpScheduler.cpp (86%) rename controller/tea_poor/lib/{WaterPump => WaterPumpScheduler}/WaterPumpScheduler.h (79%) create mode 100644 controller/tea_poor/lib/interfaces/IEnvironment.h rename controller/tea_poor/lib/{WaterPump => interfaces}/IWaterPump.h (100%) create mode 100644 controller/tea_poor/lib/interfaces/IWaterPumpSchedulerAPI.h create mode 100644 controller/tea_poor/test/test_native/main.cpp create mode 100644 controller/tea_poor/test/test_native/tests/CommandProcessor_test.h rename controller/tea_poor/test/test_native/{WaterPumpScheduler_test.cpp => tests/WaterPumpScheduler_test.h} (67%) create mode 100644 controller/tea_poor/test/test_native/tests/mocks/FakeEnvironment.h create mode 100644 controller/tea_poor/test/test_native/tests/mocks/FakeWaterPump.h create mode 100644 controller/tea_poor/test/test_native/tests/mocks/FakeWaterPumpSchedulerAPI.h diff --git a/controller/tea_poor/lib/Arduino/ArduinoEnvironment.h b/controller/tea_poor/lib/Arduino/ArduinoEnvironment.h new file mode 100644 index 0000000..bbb5a01 --- /dev/null +++ b/controller/tea_poor/lib/Arduino/ArduinoEnvironment.h @@ -0,0 +1,15 @@ +// Arduino environment +#ifndef ARDUINO_ENVIRONMENT_H +#define ARDUINO_ENVIRONMENT_H + +#include +#include + +class ArduinoEnvironment : public IEnvironment { + public: + unsigned long time() const override { + return millis(); + } +}; + +#endif // ARDUINO_ENVIRONMENT_H \ No newline at end of file diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp b/controller/tea_poor/lib/Arduino/RemoteControl.cpp similarity index 100% rename from controller/tea_poor/lib/RemoteControl/RemoteControl.cpp rename to controller/tea_poor/lib/Arduino/RemoteControl.cpp diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.h b/controller/tea_poor/lib/Arduino/RemoteControl.h similarity index 100% rename from controller/tea_poor/lib/RemoteControl/RemoteControl.h rename to controller/tea_poor/lib/Arduino/RemoteControl.h diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp b/controller/tea_poor/lib/Arduino/WaterPumpController.cpp similarity index 100% rename from controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp rename to controller/tea_poor/lib/Arduino/WaterPumpController.cpp diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h b/controller/tea_poor/lib/Arduino/WaterPumpController.h similarity index 100% rename from controller/tea_poor/lib/WaterPumpController/WaterPumpController.h rename to controller/tea_poor/lib/Arduino/WaterPumpController.h diff --git a/controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp b/controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp new file mode 100644 index 0000000..c776abf --- /dev/null +++ b/controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp @@ -0,0 +1,58 @@ +#include "CommandProcessor.h" +#include +#include + +bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) { + const int len = strlen(str); + if (len < 1) return false; + // check that string contains only digits + // first character can be '-' for negative numbers + if ((str[0] != '-') && !isdigit(str[0])) return false; + for (int i = 1; i < len; i++) { + if (!isdigit(str[i])) return false; + } + // check that number is in range + const int value = atoi(str); + if (value < minValue) return false; + if (maxValue <= value) return false; + return true; +} + +std::string CommandProcessor::status() { + std::stringstream response; + response << "{"; + // send water threshold + response << "\"water threshold\": " << _waterPumpSafeThreshold << ", "; + // send water pump status + const auto waterPumpStatus = _waterPump->status(); + const auto now = _env->time(); + const auto timeLeft = waterPumpStatus.isRunning ? waterPumpStatus.stopTime - now : 0; + response + << "\"pump\": {" + << " \"running\": " << (waterPumpStatus.isRunning ? "true, " : "false, ") + << " \"time left\": " << timeLeft + << "}"; + // end of water pump status + /////////////////////////////////// + // send remote control status + // response << "\"remote control\": " << remoteControl.asJSONString(); + // end of JSON + response << "}"; + + return response.str(); +} + +std::string CommandProcessor::pour_tea(const char *milliseconds) { + if (!isValidIntNumber(milliseconds, _waterPumpSafeThreshold)) { + // send error message as JSON + return std::string("{ \"error\": \"invalid milliseconds value\" }"); + } + // start pouring tea + _waterPump->start( atoi(milliseconds), _env->time() ); + return status(); +} + +std::string CommandProcessor::stop() { + _waterPump->stop(); + return status(); +} \ No newline at end of file diff --git a/controller/tea_poor/lib/CommandProcessor/CommandProcessor.h b/controller/tea_poor/lib/CommandProcessor/CommandProcessor.h new file mode 100644 index 0000000..a7770a0 --- /dev/null +++ b/controller/tea_poor/lib/CommandProcessor/CommandProcessor.h @@ -0,0 +1,30 @@ +// CommandProcessor class definition +#ifndef COMMANDPROCESSOR_H +#define COMMANDPROCESSOR_H + +#include +#include +#include + +// This class is used to process incoming commands +class CommandProcessor { + public: + CommandProcessor( + int waterPumpSafeThreshold, + const IEnvironmentPtr env, + const IWaterPumpSchedulerAPIPtr waterPump + ) : + _waterPumpSafeThreshold(waterPumpSafeThreshold), + _env(env), + _waterPump(waterPump) + {} + + std::string status(); + std::string pour_tea(const char *milliseconds); + std::string stop(); + private: + const int _waterPumpSafeThreshold; + const IEnvironmentPtr _env; + const IWaterPumpSchedulerAPIPtr _waterPump; +}; +#endif // COMMANDPROCESSOR_H \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp b/controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.cpp similarity index 86% rename from controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp rename to controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.cpp index eb9ae02..edf3634 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp +++ b/controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.cpp @@ -29,9 +29,6 @@ void WaterPumpScheduler::tick(unsigned long currentTimeMs) { } } -WaterPumpScheduler::WaterPumpStatus WaterPumpScheduler::status() { - return { - _waterPump->isRunning(), - _stopTime - }; +WaterPumpStatus WaterPumpScheduler::status() { + return WaterPumpStatus(_waterPump->isRunning(), _stopTime); } \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h b/controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.h similarity index 79% rename from controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h rename to controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.h index 84a6145..4e8aa66 100644 --- a/controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h +++ b/controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.h @@ -1,12 +1,13 @@ #ifndef WATERPUMPSCHEDULER_H #define WATERPUMPSCHEDULER_H -#include "IWaterPump.h" +#include +#include // This class is responsible for scheduling water pump // It is used to make sure that water pump is running for a limited time // It is also ensuring that water pump is stopped if not needed -class WaterPumpScheduler { +class WaterPumpScheduler : public IWaterPumpSchedulerAPI { private: IWaterPumpPtr _waterPump; unsigned long _stopTime = 0; @@ -18,16 +19,12 @@ class WaterPumpScheduler { ~WaterPumpScheduler(); void setup(); - void stop(); // for simplicity and testability we are passing current time as parameter - void start(unsigned long runTimeMs, unsigned long currentTimeMs); void tick(unsigned long currentTimeMs); - // pump status - struct WaterPumpStatus { - bool isRunning; - unsigned long stopTime; - }; - WaterPumpStatus status(); + // Public API + void start(unsigned long runTimeMs, unsigned long currentTimeMs) override; + void stop() override; + WaterPumpStatus status() override; }; #endif \ No newline at end of file diff --git a/controller/tea_poor/lib/interfaces/IEnvironment.h b/controller/tea_poor/lib/interfaces/IEnvironment.h new file mode 100644 index 0000000..1992884 --- /dev/null +++ b/controller/tea_poor/lib/interfaces/IEnvironment.h @@ -0,0 +1,13 @@ +#ifndef IENVIRONMENT_H +#define IENVIRONMENT_H + +#include + +class IEnvironment { + public: + virtual unsigned long time() const = 0; + virtual ~IEnvironment() {} +}; + +typedef std::shared_ptr IEnvironmentPtr; +#endif // IENVIRONMENT_H \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPump/IWaterPump.h b/controller/tea_poor/lib/interfaces/IWaterPump.h similarity index 100% rename from controller/tea_poor/lib/WaterPump/IWaterPump.h rename to controller/tea_poor/lib/interfaces/IWaterPump.h diff --git a/controller/tea_poor/lib/interfaces/IWaterPumpSchedulerAPI.h b/controller/tea_poor/lib/interfaces/IWaterPumpSchedulerAPI.h new file mode 100644 index 0000000..b82df7d --- /dev/null +++ b/controller/tea_poor/lib/interfaces/IWaterPumpSchedulerAPI.h @@ -0,0 +1,30 @@ +// IWaterPumpSchedulerAPI interface +#ifndef IWATERPUMPSCHEDULERAPI_H +#define IWATERPUMPSCHEDULERAPI_H + +#include +// pump status +struct WaterPumpStatus { +public: + bool isRunning; + unsigned long stopTime; + // copy constructor + WaterPumpStatus(const WaterPumpStatus &other) { + isRunning = other.isRunning; + stopTime = other.stopTime; + } + WaterPumpStatus(bool isRunning, unsigned long stopTime) : isRunning(isRunning), stopTime(stopTime) {} + // default constructor + WaterPumpStatus() : isRunning(false), stopTime(0) {} +}; + +class IWaterPumpSchedulerAPI { +public: + virtual ~IWaterPumpSchedulerAPI() {} + virtual void stop() = 0; + virtual void start(unsigned long runTimeMs, unsigned long currentTimeMs) = 0; + virtual WaterPumpStatus status() = 0; +}; + +using IWaterPumpSchedulerAPIPtr = std::shared_ptr; +#endif \ No newline at end of file diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index 82d27b9..fd827a9 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -28,9 +28,7 @@ build_flags = -Wall -Wextra -Wunused -static -static-libgcc -static-libstdc++ ; ignore libraries that are only for the Arduino -lib_ignore = - RemoteControl - WaterPumpController +lib_ignore = Arduino test_ignore = test_uno_r4_wifi [platformio] diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index b439390..3023273 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -3,94 +3,56 @@ #include #include #include +#include #include "secrets.h" #include +#include + +IEnvironmentPtr env = std::make_shared(); + // Setting up water pump -WaterPumpScheduler waterPump( +auto waterPump = std::make_shared( std::make_shared( WATER_PUMP_DIRECTION_PIN, WATER_PUMP_BRAKE_PIN, WATER_PUMP_POWER_PIN ) ); -// Just for safety reasons, we don't want to pour tea for too long -// Their is no reason to make it configurable and add unnecessary complexity -const int WATER_PUMP_SAFE_THRESHOLD = 10 * 1000; // setting up remote control RemoteControl remoteControl(WIFI_SSID, WIFI_PASSWORD); -void _sendSystemStatus(std::ostream& response) { - response << "{"; - // send water threshold - response << "\"water threshold\": " << WATER_PUMP_SAFE_THRESHOLD << ","; - // send water pump status - const auto waterPumpStatus = waterPump.status(); - const unsigned long timeLeft = - waterPumpStatus.isRunning ? - waterPumpStatus.stopTime - millis() : - 0; - response - << "\"pump\": {" - << " \"running\": " << (waterPumpStatus.isRunning ? "true, " : "false, ") - << " \"time left\": " << timeLeft - << "}"; - // end of water pump status - /////////////////////////////////// - // send remote control status - response << "\"remote control\": " << remoteControl.asJSONString(); - // end of JSON - response << "}"; -} - -bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) { - if (strlen(str) < 1) return false; - const int value = atoi(str); - if (value < minValue) return false; - if (maxValue <= value) return false; - return true; -} - -void pour_tea(const char *milliseconds, std::ostream &res) { - if (!isValidIntNumber(milliseconds, WATER_PUMP_SAFE_THRESHOLD)) { - // send error message as JSON - res << "{ \"error\": \"invalid milliseconds value\" }"; - return; - } - // start pouring tea - waterPump.start( atoi(milliseconds), millis() ); - _sendSystemStatus(res); -} +// build command processor +CommandProcessor commandProcessor( + WATER_PUMP_SAFE_THRESHOLD, + env, + waterPump +); void setup() { Serial.begin(9600); - waterPump.setup(); - // TODO: find a way to remove redundant code with string streams + waterPump->setup(); remoteControl.setup([](Application &app) { app.get("/pour_tea", [](Request &req, Response &res) { char milliseconds[64]; req.query("milliseconds", milliseconds, 64); - std::stringstream response; - pour_tea(milliseconds, response); - res.println(response.str().c_str()); + const auto response = commandProcessor.pour_tea(milliseconds); + res.print(response.c_str()); }); // stop water pump app.get("/stop", [](Request &req, Response &res) { - waterPump.stop(); - std::stringstream response; - _sendSystemStatus(response); - res.println(response.str().c_str()); + const auto response = commandProcessor.stop(); + res.print(response.c_str()); }); // get system status app.get("/status", [](Request &req, Response &res) { - std::stringstream response; - _sendSystemStatus(response); - res.println(response.str().c_str()); + const auto response = commandProcessor.status(); + res.print(response.c_str()); }); }); } void loop() { - waterPump.tick(millis()); + waterPump->tick(millis()); remoteControl.process(); }; \ No newline at end of file diff --git a/controller/tea_poor/src/secrets.h.example b/controller/tea_poor/src/secrets.h.example index 84d12c9..4fe08d5 100644 --- a/controller/tea_poor/src/secrets.h.example +++ b/controller/tea_poor/src/secrets.h.example @@ -11,4 +11,8 @@ const int WATER_PUMP_DIRECTION_PIN = 12; const int WATER_PUMP_BRAKE_PIN = 9; const int WATER_PUMP_POWER_PIN = 3; +// Just for safety reasons, we don't want to pour tea for too long +// Their is no reason to make it configurable and add unnecessary complexity +const int WATER_PUMP_SAFE_THRESHOLD = 10 * 1000; + #endif // SECRETS_H \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/main.cpp b/controller/tea_poor/test/test_native/main.cpp new file mode 100644 index 0000000..b6f09e9 --- /dev/null +++ b/controller/tea_poor/test/test_native/main.cpp @@ -0,0 +1,13 @@ +#include + +// include tests +#include "tests/WaterPumpScheduler_test.h" +#include "tests/CommandProcessor_test.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); // Intentionally ignoring the return value + (void)result; // Silence unused variable warning + // Always return zero-code and allow PlatformIO to parse results + return 0; +} \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/tests/CommandProcessor_test.h b/controller/tea_poor/test/test_native/tests/CommandProcessor_test.h new file mode 100644 index 0000000..291f814 --- /dev/null +++ b/controller/tea_poor/test/test_native/tests/CommandProcessor_test.h @@ -0,0 +1,79 @@ +#include +#include +#include "mocks/FakeWaterPumpSchedulerAPI.h" +#include "mocks/FakeEnvironment.h" + +// test that pour_tea() method returns error message if milliseconds: +// - greater than threshold +// - less than 0 +// - empty string +// - not a number +TEST(CommandProcessor, pour_tea_invalid_milliseconds) { + const auto EXPECTED_ERROR_MESSAGE = "{ \"error\": \"invalid milliseconds value\" }"; + CommandProcessor commandProcessor(123, nullptr, nullptr); + + // array of invalid parameters + const char *PARAMS[] = { "1234", "-1", "", "abc" }; + for (auto param : PARAMS) { + const auto response = commandProcessor.pour_tea(param); + ASSERT_EQ(response, EXPECTED_ERROR_MESSAGE); + } +} + +// test that start pouring tea by calling pour_tea() method and its stops after T milliseconds +TEST(CommandProcessor, pour_tea) { + auto env = std::make_shared(); + env->time(2343); + auto waterPump = std::make_shared(); + CommandProcessor commandProcessor(10000, env, waterPump); + const auto response = commandProcessor.pour_tea("1234"); + ASSERT_EQ(waterPump->_log, "start(1234, 2343)\n"); +} + +// test that stop() method stops pouring tea +TEST(CommandProcessor, stop) { + auto env = std::make_shared(); + auto waterPump = std::make_shared(); + CommandProcessor commandProcessor(123, env, waterPump); + const auto response = commandProcessor.stop(); + ASSERT_EQ(waterPump->_log, "stop()\n"); +} + +// test that status() method returns JSON string with water pump status +TEST(CommandProcessor, status) { + auto env = std::make_shared(); + auto waterPump = std::make_shared(); + CommandProcessor commandProcessor(123, env, waterPump); + const auto response = commandProcessor.status(); + ASSERT_EQ(response, "{" + "\"water threshold\": 123, " + "\"pump\": {" + " \"running\": false, " + " \"time left\": 0" + "}" + "}" + ); +} + +// test that status() method returns JSON string with actual time left +TEST(CommandProcessor, status_running) { + auto env = std::make_shared(); + auto waterPump = std::make_shared(); + CommandProcessor commandProcessor(12345, env, waterPump); + + commandProcessor.pour_tea("1123"); + + env->time(123); + waterPump->_status.isRunning = true; + waterPump->_status.stopTime = 1123; + + const auto response = commandProcessor.status(); + ASSERT_EQ(response, "{" + "\"water threshold\": 12345, " + "\"pump\": {" + " \"running\": true, " + " \"time left\": 1000" + "}" + "}" + ); +} \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp b/controller/tea_poor/test/test_native/tests/WaterPumpScheduler_test.h similarity index 67% rename from controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp rename to controller/tea_poor/test/test_native/tests/WaterPumpScheduler_test.h index 2b3b40f..138541b 100644 --- a/controller/tea_poor/test/test_native/WaterPumpScheduler_test.cpp +++ b/controller/tea_poor/test/test_native/tests/WaterPumpScheduler_test.h @@ -1,22 +1,7 @@ -// I wasn't able to run tests at all. Run them locally and confirm that they are working. -// Its either a local problem or a problem with the configuration of the project. -// Further goes a sketch of the tests, but I wasn't able to run them. #include +#include "mocks/FakeWaterPump.h" #include -// Fake water pump -class FakeWaterPump : public IWaterPump { -private: - bool _isRunning = false; -public: - void setup() override { _isRunning = false; } - void start() override { _isRunning = true; } - void stop() override { _isRunning = false; } - - bool isRunning() const override { return _isRunning; } -}; -// End of fake water pump - // test that pump is stopping after given time TEST(WaterPumpScheduler, test_pump_stops_after_given_time) { // random time between 1 and 10 seconds @@ -61,12 +46,4 @@ TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_ti waterPumpScheduler.tick(currentTimeMs); ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped } -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - int result = RUN_ALL_TESTS(); // Intentionally ignoring the return value - (void)result; // Silence unused variable warning - // Always return zero-code and allow PlatformIO to parse results - return 0; } \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/tests/mocks/FakeEnvironment.h b/controller/tea_poor/test/test_native/tests/mocks/FakeEnvironment.h new file mode 100644 index 0000000..51407f8 --- /dev/null +++ b/controller/tea_poor/test/test_native/tests/mocks/FakeEnvironment.h @@ -0,0 +1,19 @@ +#ifndef FAKE_ENVIRONMENT_H +#define FAKE_ENVIRONMENT_H + +#include + +class FakeEnvironment : public IEnvironment { +public: + unsigned long time() const override { + return _time; + } + + void time(unsigned long time) { + _time = time; + } +private: + unsigned long _time = 0; +}; + +#endif \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPump.h b/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPump.h new file mode 100644 index 0000000..2dcf154 --- /dev/null +++ b/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPump.h @@ -0,0 +1,18 @@ +#ifndef FAKE_WATER_PUMP_H +#define FAKE_WATER_PUMP_H + +#include + +// Fake water pump +class FakeWaterPump : public IWaterPump { +private: + bool _isRunning = false; +public: + void setup() override { _isRunning = false; } + void start() override { _isRunning = true; } + void stop() override { _isRunning = false; } + + bool isRunning() const override { return _isRunning; } +}; + +#endif // FAKE_WATER_PUMP_H \ No newline at end of file diff --git a/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPumpSchedulerAPI.h b/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPumpSchedulerAPI.h new file mode 100644 index 0000000..896046a --- /dev/null +++ b/controller/tea_poor/test/test_native/tests/mocks/FakeWaterPumpSchedulerAPI.h @@ -0,0 +1,26 @@ +// FakeWaterPumpSchedulerAPI.h is a mock class for WaterPumpSchedulerAPI.h +#ifndef FAKE_WATER_PUMP_SCHEDULER_API_H +#define FAKE_WATER_PUMP_SCHEDULER_API_H + +#include +#include + +class FakeWaterPumpSchedulerAPI : public IWaterPumpSchedulerAPI { +public: + void stop() override { + _log += "stop()\n"; + } + + void start(unsigned long runTimeMs, unsigned long currentTimeMs) override { + _log += "start(" + std::to_string(runTimeMs) + ", " + std::to_string(currentTimeMs) + ")\n"; + } + + WaterPumpStatus status() override { + return _status; + } + + WaterPumpStatus _status; + std::string _log; +}; + +#endif \ No newline at end of file From eade444d59ae3b9dcb1b0f86d5f26e150d68f059 Mon Sep 17 00:00:00 2001 From: GreenWizard Date: Fri, 5 Jan 2024 08:14:27 +0100 Subject: [PATCH 21/21] prevent CORS issue...? --- controller/tea_poor/src/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index 3023273..7509e0b 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -28,6 +28,13 @@ CommandProcessor commandProcessor( waterPump ); +void withExtraHeaders(Response &res) { + res.set("Access-Control-Allow-Origin", "*"); + res.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); + res.set("Access-Control-Allow-Headers", "Content-Type"); + res.set("Content-Type", "application/json"); +} + void setup() { Serial.begin(9600); waterPump->setup(); @@ -37,16 +44,19 @@ void setup() { req.query("milliseconds", milliseconds, 64); const auto response = commandProcessor.pour_tea(milliseconds); + withExtraHeaders(res); res.print(response.c_str()); }); // stop water pump app.get("/stop", [](Request &req, Response &res) { const auto response = commandProcessor.stop(); + withExtraHeaders(res); res.print(response.c_str()); }); // get system status app.get("/status", [](Request &req, Response &res) { const auto response = commandProcessor.status(); + withExtraHeaders(res); res.print(response.c_str()); }); });