From f98524f5317250033c3163e04993c7603803fdfe Mon Sep 17 00:00:00 2001 From: Aurelien Labrosse Date: Thu, 30 Nov 2023 22:51:12 +0000 Subject: [PATCH] wip --- lib/Domain/Game.hpp | 2 +- lib/Domain/ITargetHost.hpp | 104 ------------------------- lib/Domain/{ => Target}/BTEGui.cpp | 2 +- lib/Domain/{ => Target}/BTEGui.hpp | 2 +- lib/Domain/Target/ITarget.hpp | 50 ++++++++++++ lib/Domain/Target/ITargetHost.hpp | 48 ++++++++++++ lib/Domain/{ => Target}/ITargetUi.hpp | 0 lib/Domain/Target/LDRTarget.cpp | 49 ++++++++++++ lib/Domain/Target/LDRTarget.hpp | 53 +++++++++++++ lib/Domain/{ => Target}/TargetHost.cpp | 55 +++++-------- lib/Domain/{ => Target}/TargetHost.hpp | 25 +++--- lib/Native/TestGui.hpp | 2 +- src/ConsoleTargets.cpp | 2 +- src/TargetApp.cpp | 4 +- test/native/test_Target/target.cpp | 54 ++++++------- 15 files changed, 268 insertions(+), 184 deletions(-) delete mode 100644 lib/Domain/ITargetHost.hpp rename lib/Domain/{ => Target}/BTEGui.cpp (99%) rename lib/Domain/{ => Target}/BTEGui.hpp (98%) create mode 100644 lib/Domain/Target/ITarget.hpp create mode 100644 lib/Domain/Target/ITargetHost.hpp rename lib/Domain/{ => Target}/ITargetUi.hpp (100%) create mode 100644 lib/Domain/Target/LDRTarget.cpp create mode 100644 lib/Domain/Target/LDRTarget.hpp rename lib/Domain/{ => Target}/TargetHost.cpp (80%) rename lib/Domain/{ => Target}/TargetHost.hpp (83%) diff --git a/lib/Domain/Game.hpp b/lib/Domain/Game.hpp index d60cd8f..316a091 100644 --- a/lib/Domain/Game.hpp +++ b/lib/Domain/Game.hpp @@ -16,7 +16,7 @@ */ #pragma once -#include +#include #include #include diff --git a/lib/Domain/ITargetHost.hpp b/lib/Domain/ITargetHost.hpp deleted file mode 100644 index b014526..0000000 --- a/lib/Domain/ITargetHost.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * Copyright (c) 2023 Aurélien Labrosse - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include - -enum TargetState { Calibrating, Ready, Hit }; -enum Color { None, Red, Green, Blue }; - -class ITargetHal { - - virtual uint16_t getLuminosity() = 0; - virtual void setLedColor(Color color) = 0; -}; - -/** - * A target is considered hit if its - * luminosity value exceed (ambientValue + threshold) - * A target has a multicolor led: Off is ready, green is shot, blue is - * calibration, red is error - */ -class Target : public ITargetHal { - - TargetState state; - uint8_t luminosityPin; - -public: - uint16_t ambientValue; - uint8_t index; - Target(uint8_t luminosityPin luminosityPin) - : ambientValue(0), luminosityPin(luminosityPin) { - ready(); - } - - TargetState getState() { return state; } - - void hit() { - state = Hit; - setLedColor(Green); - } - - void ready() { - state = Ready; - setLedColor(None); - } - - void calibrate() { - state = Calibrating; - setLedColor(Blue); - getLuminosity(); - ambientValue = getLuminosity(); - ambientValue += getLuminosity(); - ambientValue += getLuminosity(); - ambientValue += getLuminosity(); - ambientValue /= 4; - ready(); - } - - uint16_t getLuminosity() override { return analogRead(luminosityPin); } - void setLedColor(Color color) override{}; -}; - -class ITargetHost { -public: - virtual ~ITargetHost() {} - - /** - * Return light intensity for given target - * value [0;1024] - */ - virtual uint16_t getLightIntensityFor(const Target &target) = 0; - - /** - * Return value of difference needed between ambient light level and hit light - * level value [0;254] - */ - virtual uint8_t getThreshold() = 0; - - virtual void resetTargets() = 0; - - /** - * Notification led - */ - virtual void ledOn() = 0; - - /** - * Notification led - */ - virtual void ledOff() = 0; -}; \ No newline at end of file diff --git a/lib/Domain/BTEGui.cpp b/lib/Domain/Target/BTEGui.cpp similarity index 99% rename from lib/Domain/BTEGui.cpp rename to lib/Domain/Target/BTEGui.cpp index 04f7e48..c3c90e6 100644 --- a/lib/Domain/BTEGui.cpp +++ b/lib/Domain/Target/BTEGui.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include +#include #if not defined(NATIVE) #include diff --git a/lib/Domain/BTEGui.hpp b/lib/Domain/Target/BTEGui.hpp similarity index 98% rename from lib/Domain/BTEGui.hpp rename to lib/Domain/Target/BTEGui.hpp index 94f1b28..2b7090e 100644 --- a/lib/Domain/BTEGui.hpp +++ b/lib/Domain/Target/BTEGui.hpp @@ -16,7 +16,7 @@ */ #pragma once -#include +#include #include #if not defined(AVR) diff --git a/lib/Domain/Target/ITarget.hpp b/lib/Domain/Target/ITarget.hpp new file mode 100644 index 0000000..6ad5557 --- /dev/null +++ b/lib/Domain/Target/ITarget.hpp @@ -0,0 +1,50 @@ + +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +class ITarget { + +public: + enum State { Calibrating, Ready, Hit, Error }; + + virtual ~ITarget() {} + + /** + * @brief transition to Ready state + * + */ + virtual void reset() = 0; + + /** + * @brief transition to Calibration state + * + */ + virtual void calibrate() = 0; + + /** + * @brief transition to Hit state + * + */ + virtual void hit() = 0; + + virtual uint16_t getLuminosity() = 0; + + virtual State getState() = 0; +}; \ No newline at end of file diff --git a/lib/Domain/Target/ITargetHost.hpp b/lib/Domain/Target/ITargetHost.hpp new file mode 100644 index 0000000..6ca59f4 --- /dev/null +++ b/lib/Domain/Target/ITargetHost.hpp @@ -0,0 +1,48 @@ +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +/** + * @brief Target host is a device managing 5 targets + * + * A target is considered hit if its luminosity value exceed (ambientValue + + * threshold) A target has a multicolor led: Off is ready, green is shot, blue + * is calibration, red is error + */ + +class ITargetHost { + + +public: + + virtual ~ITargetHost() {} + + virtual void reset() = 0; + + /** + * Notification led on + */ + virtual void ledOn() = 0; + + /** + * Notification led off + */ + virtual void ledOff() = 0; +}; \ No newline at end of file diff --git a/lib/Domain/ITargetUi.hpp b/lib/Domain/Target/ITargetUi.hpp similarity index 100% rename from lib/Domain/ITargetUi.hpp rename to lib/Domain/Target/ITargetUi.hpp diff --git a/lib/Domain/Target/LDRTarget.cpp b/lib/Domain/Target/LDRTarget.cpp new file mode 100644 index 0000000..c0e879a --- /dev/null +++ b/lib/Domain/Target/LDRTarget.cpp @@ -0,0 +1,49 @@ +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +LDRTarget::LDRTarget(uint8_t luminosityPin) + : ambiantLuminosity(0), luminosityPin(luminosityPin), state(Ready), + threshold(0) {} + +void LDRTarget::reset() { state = Ready; } + +void LDRTarget::calibrate() { + state = Calibrating; + getLuminosity(); + ambiantLuminosity = getLuminosity(); + ambiantLuminosity += getLuminosity(); + ambiantLuminosity += getLuminosity(); + ambiantLuminosity += getLuminosity(); + ambiantLuminosity /= 4; + reset(); +} + +boolean LDRTarget::check() { + return (getLuminosity() > (ambiantLuminosity + threshold)); +} + +void LDRTarget::hit() { state = Hit; } + +void LDRTarget::setThreshold(uint8_t threshold) { this->threshold = threshold; } + +uint16_t LDRTarget::getLuminosity() { + return analogRead(luminosityPin); +} + +ITarget::State LDRTarget::getState() { return state; } \ No newline at end of file diff --git a/lib/Domain/Target/LDRTarget.hpp b/lib/Domain/Target/LDRTarget.hpp new file mode 100644 index 0000000..6279562 --- /dev/null +++ b/lib/Domain/Target/LDRTarget.hpp @@ -0,0 +1,53 @@ + +/* + * + * Copyright (c) 2023 Aurélien Labrosse + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +// forward declaration +class TargetHost; + +class LDRTarget : public ITarget { + + uint16_t ambiantLuminosity; + uint8_t luminosityPin; + State state; + uint8_t threshold; + +public: + LDRTarget(uint8_t luminosityPin); + + void reset() override; + void calibrate() override; + void hit() override; + uint16_t getLuminosity() override; + State getState() override; + + /** + * @brief Set the difference with ambiant luminosity level that triggers a hit + * condition + * + * @param threshold + */ + void setThreshold(uint8_t threshold); + + /** + * @brief Check if target is in hit condition + * + */ + bool check(); +}; \ No newline at end of file diff --git a/lib/Domain/TargetHost.cpp b/lib/Domain/Target/TargetHost.cpp similarity index 80% rename from lib/Domain/TargetHost.cpp rename to lib/Domain/Target/TargetHost.cpp index b2419d6..63249a2 100644 --- a/lib/Domain/TargetHost.cpp +++ b/lib/Domain/Target/TargetHost.cpp @@ -14,10 +14,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "TargetHost.hpp" #include #include - +#include +#include #include char serial_command_buffer_[16]; @@ -59,10 +59,6 @@ void TargetHost::storeThreshold(uint8_t threshold) { EEPROM.update(TRESHOLD_ADDRESS, threshold); } -inline uint16_t TargetHost::getLightIntensityFor(const Target &target) { - return analogRead(target.pin); -} - void TargetHost::ledOn() { digitalWrite(LED_PIN, HIGH); } void TargetHost::ledOff() { digitalWrite(LED_PIN, LOW); } @@ -77,9 +73,8 @@ void TargetHost::setup() { // load value in cache from EEPROM getThreshold(); - for (Target &target : targets) { - analogRead(target.pin); - target.ambientValue = analogRead(target.pin); + for (ITarget *target : targets) { + target->calibrate(); } _serial_commands.context = this; _serial_commands.SetDefaultHandler(serial_cmd_unrecognized_callback); @@ -92,19 +87,16 @@ void TargetHost::setup() { void TargetHost::loop() { - uint16_t threshold = getThreshold(); - - for (Target &target : targets) { - if (getLightIntensityFor(target) > (target.ambientValue + threshold)) { + static const uint8_t targetCount = (sizeof(targets) / sizeof(ITarget)); + for (uint8_t targetIndex = 0; targetIndex < targetCount; targetIndex++) { + ITarget *target = targets[targetIndex]; + if (target->getState() == ITarget::State::Hit) { ITargetUi::TARGET uiTarget; - uiTarget = static_cast(target.index); - if (!target.isHit) { - ui->hitTarget(uiTarget); - target.isHit = true; - ledOn(); - delay(100); - ledOff(); - } + uiTarget = static_cast(targetIndex); + ui->hitTarget(uiTarget); + ledOn(); + delay(100); + ledOff(); } } _serial_commands.ReadSerial(); @@ -142,26 +134,17 @@ void serial_cmd_changePlayer_callback(SerialCommands *sender) { targetHost->game->changeCurrentPlayerTo(playerId); } -void TargetHost::resetTargets() { ui->resetTargets(); } +void TargetHost::reset() { ui->resetTargets(); } TargetHost::TargetHost(Game *game, ITargetUi *ui) { - targets[0].pin = TARGET_A_PIN; - targets[0].index = 0; - - targets[1].pin = TARGET_Z_PIN; - targets[1].index = 1; - - targets[2].pin = TARGET_E_PIN; - targets[2].index = 2; - - targets[3].pin = TARGET_R_PIN; - targets[3].index = 3; - - targets[4].pin = TARGET_T_PIN; - targets[4].index = 4; this->game = game; this->ui = ui; thresholdCache = 0; + targets[0] = new LDRTarget(A0); + targets[1] = new LDRTarget(A1); + targets[2] = new LDRTarget(A2); + targets[3] = new LDRTarget(A3); + targets[4] = new LDRTarget(A4); } \ No newline at end of file diff --git a/lib/Domain/TargetHost.hpp b/lib/Domain/Target/TargetHost.hpp similarity index 83% rename from lib/Domain/TargetHost.hpp rename to lib/Domain/Target/TargetHost.hpp index eb5cd47..1376de1 100644 --- a/lib/Domain/TargetHost.hpp +++ b/lib/Domain/Target/TargetHost.hpp @@ -17,8 +17,8 @@ #pragma once #include -#include -#include +#include +#include #include /** @@ -34,26 +34,29 @@ class TargetHost : public ITargetHost { * in same luminosity condition without reconfiguring */ uint8_t thresholdCache; - + public: - Target targets[5]; + void storeThreshold(uint8_t threshold); + + /** + * Return value of difference needed between ambient light level and hit light + * level value [1;254] + */ + + uint8_t getThreshold(); + + ITarget *targets[5]; Game *game; ITargetUi *ui; TargetHost(Game *game, ITargetUi *ui); - /* ITargetHost API */ - - uint16_t getLightIntensityFor(const Target &target) override; void ledOn() override; void ledOff() override; - void resetTargets() override; - uint8_t getThreshold() override; + void reset() override; /* Specific methods */ void setup(); void loop(); - void storeThreshold(uint8_t threshold); - }; \ No newline at end of file diff --git a/lib/Native/TestGui.hpp b/lib/Native/TestGui.hpp index ba44d1f..8498c15 100644 --- a/lib/Native/TestGui.hpp +++ b/lib/Native/TestGui.hpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ #pragma once -#include +#include #include class TestGui : public ITargetUi { diff --git a/src/ConsoleTargets.cpp b/src/ConsoleTargets.cpp index 6e6ee73..2d504e2 100644 --- a/src/ConsoleTargets.cpp +++ b/src/ConsoleTargets.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include +#include #include #include #include diff --git a/src/TargetApp.cpp b/src/TargetApp.cpp index 8ce43cd..561cde9 100644 --- a/src/TargetApp.cpp +++ b/src/TargetApp.cpp @@ -16,9 +16,9 @@ */ #include -#include +#include #include -#include +#include BTEGui gui; Game game(&gui); diff --git a/test/native/test_Target/target.cpp b/test/native/test_Target/target.cpp index f276a1e..d69e20f 100644 --- a/test/native/test_Target/target.cpp +++ b/test/native/test_Target/target.cpp @@ -19,8 +19,8 @@ using namespace fakeit; #include -#include -#include +#include +#include #include @@ -57,8 +57,9 @@ void expect_threshold_to_be_storable_in_eeprom() { void expect_setup_to_configure_led_pin() { Mock mockGui; - Game game(&mockGui.get()); - TargetHost app(&game, &mockGui.get()); + ITargetUi &ui = mockGui.get(); + Game game(&ui); + TargetHost app(&game, &ui); When(Method(ArduinoFake(), pinMode)).Return(); When(Method(ArduinoFake(), analogRead)).AlwaysReturn(0); When(Method(ArduinoFake(), digitalWrite)).AlwaysReturn(); @@ -68,7 +69,7 @@ void expect_setup_to_configure_led_pin() { mockGuiForSetup(mockGui); - app.setup(); + app.setup(); static const uint8_t LED_PIN = 9; Verify(Method(ArduinoFake(), pinMode).Using(LED_PIN, OUTPUT)).Once(); @@ -76,33 +77,30 @@ void expect_setup_to_configure_led_pin() { void expect_ambient_level_to_be_sampled_at_startup_for_each_target() { Mock mockGui; - Game game(&mockGui.get()); - TargetHost app(&game, &mockGui.get()); + ITargetUi &ui = mockGui.get(); + Game game(&ui); + TargetHost app(&game, &ui); When(Method(ArduinoFake(), pinMode)).Return(); When(MOCKED_EEPROM_READ).AlwaysReturn(0); When(OverloadedMethod(ArduinoFake(Serial), begin, void(unsigned long))) .AlwaysReturn(); + When(Method(ArduinoFake(), digitalWrite)).AlwaysReturn(); When(Method(ArduinoFake(), analogRead)).AlwaysReturn(1000); - When(Method(ArduinoFake(), analogRead).Using(A0)).AlwaysReturn(99); - When(Method(ArduinoFake(), analogRead).Using(A1)).AlwaysReturn(108); - When(Method(ArduinoFake(), analogRead).Using(A2)).AlwaysReturn(104); - When(Method(ArduinoFake(), analogRead).Using(A3)).AlwaysReturn(103); - When(Method(ArduinoFake(), analogRead).Using(A4)).AlwaysReturn(96); mockGuiForSetup(mockGui); app.setup(); - TEST_ASSERT_EQUAL(99, app.targets[0].ambientValue); - TEST_ASSERT_EQUAL(108, app.targets[1].ambientValue); - TEST_ASSERT_EQUAL(104, app.targets[2].ambientValue); - TEST_ASSERT_EQUAL(103, app.targets[3].ambientValue); - TEST_ASSERT_EQUAL(96, app.targets[4].ambientValue); - Verify(Method(ArduinoFake(), pinMode)).AtLeastOnce(); - Verify(Method(ArduinoFake(), analogRead)).AtLeastOnce(); + + // Note: 5 reads for calibration + Verify(Method(ArduinoFake(), analogRead).Using(A0)).Exactly(5); + Verify(Method(ArduinoFake(), analogRead).Using(A1)).Exactly(5); + Verify(Method(ArduinoFake(), analogRead).Using(A2)).Exactly(5); + Verify(Method(ArduinoFake(), analogRead).Using(A3)).Exactly(5); + Verify(Method(ArduinoFake(), analogRead).Using(A4)).Exactly(5); } void expect_status_led_to_blink_when_a_target_is_hit() { @@ -117,8 +115,9 @@ void expect_status_led_to_blink_when_a_target_is_hit() { ArduinoFakeReset(); Mock mockGui; - Game game(&mockGui.get()); - TargetHost app(&game, &mockGui.get()); + ITargetUi &ui = mockGui.get(); + Game game(&ui); + TargetHost app(&game, &ui); // ARRANGE @@ -160,8 +159,9 @@ void expect_status_led_to_blink_when_a_target_is_hit() { void expect_gui_to_be_notified_when_a_target_is_hit() { Mock mockGui; - Game game(&mockGui.get()); - TargetHost app(&game, &mockGui.get()); + ITargetUi &ui = mockGui.get(); + Game game(&ui); + TargetHost app(&game, &ui); // Run initialization and two loop() call. // Ensure GUI is notified *once* when a target is hit @@ -216,8 +216,9 @@ void expect_gui_to_be_notified_when_a_target_is_hit() { void expect_threshold_to_be_settable_via_serial_command() { Mock mockGui; - Game game(&mockGui.get()); - TargetHost app(&game, &mockGui.get()); + ITargetUi &ui = mockGui.get(); + Game game(&ui); + TargetHost app(&game, &ui); mockForCommandTest(mockGui); @@ -272,7 +273,7 @@ int main(int, char **) { RUN_TEST(expect_gui_to_be_notified_when_a_target_is_hit); RUN_TEST(expect_threshold_to_be_settable_via_serial_command); RUN_TEST(expect_player_to_be_changeable_via_serial_command); - + UNITY_END(); return 0; } @@ -315,6 +316,7 @@ static void mockGuiForSetup(Mock &mockGui) { When(Method(mockGui, restart)).AlwaysReturn(); When(Method(mockGui, displayPlayerInfo)).AlwaysReturn(); When(Method(mockGui, setCurrentPlayer)).AlwaysReturn(); + When(Method(mockGui, resetTargets)).AlwaysReturn(); When(Method(mockGui, hitTarget)).AlwaysReturn(); When(OverloadedMethod(mockGui, log, void(const char *))).AlwaysReturn(); When(OverloadedMethod(mockGui, log, void(uint8_t))).AlwaysReturn();