From 4bd14b53b27d09f9325e16c3875559dd967b2169 Mon Sep 17 00:00:00 2001
From: Federico Lolli <federico.lolli@skywarder.eu>
Date: Thu, 23 May 2024 22:14:13 +0200
Subject: [PATCH] [ARP] Refactor of Leds as single thread

needs testing
---
 .../Groundstation/Automated/Leds/Leds.cpp     | 193 ++++++++----------
 .../Groundstation/Automated/Leds/Leds.h       |  85 ++++----
 .../Automated/SMController/SMController.cpp   |  16 +-
 .../Automated/automated-antennas-entry.cpp    |   6 +-
 4 files changed, 138 insertions(+), 162 deletions(-)

diff --git a/src/boards/Groundstation/Automated/Leds/Leds.cpp b/src/boards/Groundstation/Automated/Leds/Leds.cpp
index ff5069823..ca775eb36 100644
--- a/src/boards/Groundstation/Automated/Leds/Leds.cpp
+++ b/src/boards/Groundstation/Automated/Leds/Leds.cpp
@@ -22,20 +22,99 @@
 
 #include "Leds.h"
 
-#include <mutex>
-
 using namespace Boardcore;
 
 namespace Antennas
 {
 
-LedThread::LedThread(LedColor color)
-    : ActiveObject(miosix::STACK_DEFAULT_FOR_PTHREAD, 0), color(color),
-      state(LedState::OFF)
+Leds::Leds(TaskScheduler* scheduler) : scheduler(scheduler)
 {
+    leds_state.fill(LedState::OFF);
+    led_steps.fill(0);
 }
 
-void LedThread::ledOn(LedColor color)
+bool Leds::start()
+{
+    size_t result;
+    bool ok = true;
+
+    // turn off all leds
+    miosix::ledOff();
+
+    result = scheduler->addTask(std::bind(&Leds::update, this),
+                                LED_BLINK_FAST_PERIOD_MS,
+                                TaskScheduler::Policy::RECOVER);
+    ok &= result;
+
+    return ok;
+}
+
+void Leds::update()
+{
+    LedState state;
+    LedColor color;
+
+    for (size_t i = 0; i < leds_state.size(); i++)
+    {
+        state = leds_state[i];
+        color = static_cast<LedColor>(i);
+
+        switch (state)
+        {
+            case LedState::BLINK_SLOW:
+                if (led_steps[i]++ == 1)
+                {
+                    ledToggle(color);
+                    led_steps[i] = 0;
+                }
+                break;
+            case LedState::BLINK_FAST:
+            {
+                ledToggle(color);
+                break;
+            }
+            case LedState::ON:
+            {
+                ledOn(color);
+                break;
+            }
+            case LedState::OFF:
+            {
+                ledOff(color);
+                break;
+            }
+        }
+    }
+}
+
+void Leds::setFastBlink(LedColor color)
+{
+    *ledRef(color) = LedState::BLINK_FAST;
+}
+
+void Leds::setSlowBlink(LedColor color)
+{
+    *ledRef(color) = LedState::BLINK_SLOW;
+}
+
+void Leds::setOn(LedColor color) { *ledRef(color) = LedState::ON; }
+
+void Leds::setOff(LedColor color) { *ledRef(color) = LedState::OFF; }
+
+void Leds::endlessBlink(LedColor color)
+{
+    setSlowBlink(color);
+    miosix::Thread::wait();  // wait forever
+}
+
+void Leds::ledToggle(LedColor color)
+{
+    size_t i = static_cast<size_t>(color);
+    led_toggles[i] ? ledOn(color) : ledOff(color);
+    led_toggles[i] = !led_toggles[i];
+}
+
+void Leds::ledOn(LedColor color)
 {
 #ifdef _BOARD_STM32F767ZI_AUTOMATED_ANTENNAS
     switch (color)
@@ -72,7 +151,7 @@ void LedThread::ledOn(LedColor color)
 #endif
 }
 
-void LedThread::ledOff(LedColor color)
+void Leds::ledOff(LedColor color)
 {
 #ifdef _BOARD_STM32F767ZI_AUTOMATED_ANTENNAS
     switch (color)
@@ -109,104 +188,4 @@ void LedThread::ledOff(LedColor color)
 #endif
 }
 
-void LedThread::run()
-{
-    LedState old = state;
-    while (true)
-    {
-        std::unique_lock<std::mutex> lock(mutex);
-        cv.wait(lock, [&] { return state != old; });
-        old = state;
-        switch (state)
-        {
-            case LedState::BLINKING:
-            {
-                do
-                {
-                    ledOn(color);
-                    miosix::Thread::sleep(blinking_interval);
-                    ledOff(color);
-                    miosix::Thread::sleep(blinking_interval);
-                } while (state == LedState::BLINKING);
-                break;
-            }
-            case LedState::ON:
-            {
-                ledOn(color);
-                break;
-            }
-            case LedState::OFF:
-            {
-                ledOff(color);
-                break;
-            }
-        }
-    }
-}
-
-void LedThread::setBlinking(uint32_t ms_interval)
-{
-    {
-        std::lock_guard<std::mutex> lock(mutex);
-        state = LedState::BLINKING;
-    }
-    cv.notify_one();
-}
-
-void LedThread::setOn()
-{
-    {
-        std::lock_guard<std::mutex> lock(mutex);
-        state = LedState::ON;
-    }
-    cv.notify_one();
-}
-
-void LedThread::setOff()
-{
-    {
-        std::lock_guard<std::mutex> lock(mutex);
-        state = LedState::OFF;
-    }
-    cv.notify_one();
-}
-
-Leds::Leds()
-{
-    for (size_t i = 0; i < leds.size(); i++)
-    {
-        leds[i] = std::make_unique<LedThread>(static_cast<LedColor>(i));
-    }
-}
-
-bool Leds::start()
-{
-    bool ok = true;
-
-    // turn off all leds
-    miosix::ledOff();
-
-    for (size_t i = 0; i < leds.size(); i++)
-    {
-        ok &= leds[i]->start();
-    }
-
-    return ok;
-}
-
-void Leds::setBlinking(LedColor color, uint32_t ms_interval)
-{
-    ledRef(color)->setBlinking(ms_interval);
-}
-
-void Leds::setOn(LedColor color) { ledRef(color)->setOn(); }
-
-void Leds::setOff(LedColor color) { ledRef(color)->setOff(); }
-
-void Leds::endlessBlink(LedColor color)
-{
-    ledRef(color)->setBlinking(100);
-    miosix::Thread::wait();  // wait forever
-}
-
 }  // namespace Antennas
diff --git a/src/boards/Groundstation/Automated/Leds/Leds.h b/src/boards/Groundstation/Automated/Leds/Leds.h
index 27830e4c9..1c81ea9f2 100644
--- a/src/boards/Groundstation/Automated/Leds/Leds.h
+++ b/src/boards/Groundstation/Automated/Leds/Leds.h
@@ -23,12 +23,15 @@
 #pragma once
 
 #include <ActiveObject.h>
+#include <scheduler/TaskScheduler.h>
 
 #include <array>
-#include <condition_variable>
 #include <mutex>
 #include <utils/ModuleManager/ModuleManager.hpp>
 
+constexpr uint32_t LED_BLINK_FAST_PERIOD_MS = 50;
+constexpr uint32_t LED_BLINK_SLOW_PERIOD_MS = 100;
+
 namespace Antennas
 {
 
@@ -40,45 +43,6 @@ enum class LedColor : uint8_t
     GREEN
 };
 
-class LedThread : public Boardcore::ActiveObject
-{
-public:
-    explicit LedThread(LedColor color);
-
-    /**
-     * @brief non-blocking action to set a led to blink in a loop
-     */
-    void setBlinking(uint32_t ms_interval);
-
-    /**
-     * @brief non-blocking action to set on the led
-     */
-    void setOn();
-
-    /**
-     * @brief non-blocking action to set off the led
-     */
-    void setOff();
-
-private:
-    enum class LedState : uint8_t
-    {
-        OFF = 0,
-        ON,
-        BLINKING
-    };
-
-    void ledOn(LedColor color);
-    void ledOff(LedColor color);
-    void run() override;
-
-    LedColor color;
-    LedState state;
-    std::mutex mutex;
-    std::condition_variable cv;
-    uint32_t blinking_interval = 100;  // [ms]
-};
-
 /**
  * @brief Utility to handle blinking leds with non-blocking sleep
  * (useful for state machines states that need to blink leds without blocking)
@@ -86,7 +50,7 @@ private:
 class Leds : public Boardcore::Module
 {
 public:
-    explicit Leds();
+    explicit Leds(Boardcore::TaskScheduler* scheduler);
 
     /**
      * @brief Start all the blinking LED thread
@@ -96,7 +60,12 @@ public:
     /**
      * @brief non-blocking action to set a led to blink in a loop
      */
-    void setBlinking(LedColor color, uint32_t ms_interval);
+    void setFastBlink(LedColor color);
+
+    /**
+     * @brief non-blocking action to set a led to blink in a loop
+     */
+    void setSlowBlink(LedColor color);
 
     /**
      * @brief non-blocking action to set on a led
@@ -114,12 +83,38 @@ public:
     void endlessBlink(LedColor color);
 
 private:
-    LedThread* ledRef(LedColor color)
+    enum class LedState : uint8_t
+    {
+        OFF = 0,
+        ON,
+        BLINK_SLOW,
+        BLINK_FAST,
+    };
+
+    void ledOn(LedColor color);
+    void ledOff(LedColor color);
+    void ledToggle(LedColor color);
+
+    /**
+     * @brief Update routine called by the scheduler
+     * @details This function is called by the scheduler to update the leds
+     * state and blink them accordingly
+     */
+    void update();
+
+    LedState* ledRef(LedColor color)
     {
-        return leds[static_cast<uint8_t>(color)].get();
+        return &leds_state[static_cast<uint8_t>(color)];
     }
 
-    std::array<std::unique_ptr<LedThread>, 4> leds;
+    // scheduler to run the update blink function
+    Boardcore::TaskScheduler* scheduler;
+    // toggles to allow led to blink
+    std::array<bool, 4> led_toggles;
+    // counter to keep track of slow blink
+    std::array<uint32_t, 4> led_steps;
+    // state of the leds
+    std::array<LedState, 4> leds_state;
 };
 
 }  // namespace Antennas
diff --git a/src/boards/Groundstation/Automated/SMController/SMController.cpp b/src/boards/Groundstation/Automated/SMController/SMController.cpp
index 817829659..58676053a 100644
--- a/src/boards/Groundstation/Automated/SMController/SMController.cpp
+++ b/src/boards/Groundstation/Automated/SMController/SMController.cpp
@@ -370,8 +370,8 @@ State SMController::state_init_error(const Event& event)
         case EV_ENTRY:
         {
             logStatus(SMControllerState::INIT_ERROR);
-            ModuleManager::getInstance().get<Leds>()->setBlinking(LedColor::RED,
-                                                                  100);
+            ModuleManager::getInstance().get<Leds>()->setSlowBlink(
+                LedColor::RED);
             return HANDLED;
         }
         case EV_EXIT:
@@ -582,8 +582,8 @@ State SMController::state_fix_antennas(const Event& event)
         case EV_ENTRY:
         {
             logStatus(SMControllerState::FIX_ANTENNAS);
-            ModuleManager::getInstance().get<Leds>()->setBlinking(
-                LedColor::ORANGE, 50);
+            ModuleManager::getInstance().get<Leds>()->setFastBlink(
+                LedColor::ORANGE);
             return HANDLED;
         }
         case EV_EXIT:
@@ -622,8 +622,8 @@ State SMController::state_fix_rocket(const Event& event)
         case EV_ENTRY:
         {
             logStatus(SMControllerState::FIX_ROCKET);
-            ModuleManager::getInstance().get<Leds>()->setBlinking(
-                LedColor::YELLOW, 50);
+            ModuleManager::getInstance().get<Leds>()->setFastBlink(
+                LedColor::YELLOW);
             return HANDLED;
         }
         case EV_EXIT:
@@ -775,8 +775,8 @@ State SMController::state_fix_rocket_nf(const Event& event)
         case EV_ENTRY:
         {
             logStatus(SMControllerState::FIX_ROCKET_NF);
-            ModuleManager::getInstance().get<Leds>()->setBlinking(
-                LedColor::YELLOW, 50);
+            ModuleManager::getInstance().get<Leds>()->setFastBlink(
+                LedColor::YELLOW);
             return HANDLED;
         }
         case EV_EXIT:
diff --git a/src/entrypoints/Groundstation/Automated/automated-antennas-entry.cpp b/src/entrypoints/Groundstation/Automated/automated-antennas-entry.cpp
index 6cb204db0..3cf568bae 100644
--- a/src/entrypoints/Groundstation/Automated/automated-antennas-entry.cpp
+++ b/src/entrypoints/Groundstation/Automated/automated-antennas-entry.cpp
@@ -97,8 +97,9 @@ int main()
         });
     ButtonHandler::getInstance().start();
 
+    TaskScheduler *scheduler_low  = new TaskScheduler(0);
     TaskScheduler *scheduler_high = new TaskScheduler();
-    Leds *leds                    = new Leds();
+    Leds *leds                    = new Leds(scheduler_low);
     Hub *hub                      = new Hub();
     Buses *buses                  = new Buses();
     Serial *serial                = new Serial();
@@ -139,7 +140,8 @@ int main()
 #ifndef NO_SD_LOGGING
         START_MODULE("Logger", [&] { return Logger::getInstance().start(); });
 #endif
-        START_MODULE("Scheduler", [&] { return scheduler_high->start(); });
+        START_MODULE("Scheduler Low", [&] { return scheduler_low->start(); });
+        START_MODULE("Scheduler High", [&] { return scheduler_high->start(); });
         START_MODULE("Serial", [&] { return serial->start(); });
         START_MODULE("Main Radio", [&] { return radio_main->start(); });
         START_MODULE("Ethernet", [&] { return ethernet->start(); });
-- 
GitLab