diff --git a/.cproject b/.cproject
index c112998..400127d 100644
--- a/.cproject
+++ b/.cproject
@@ -25,6 +25,7 @@
+
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index f807aa8..69eae04 100644
--- a/.settings/language.settings.xml
+++ b/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/App/Inc/sta/config.hpp b/App/Inc/sta/config.hpp
index eee17cb..667052e 100644
--- a/App/Inc/sta/config.hpp
+++ b/App/Inc/sta/config.hpp
@@ -33,7 +33,7 @@
#define STA_TACOS_STATEMACHINE_PRIORITY osPriorityNormal
// Statemachine settings. Here, we only have a single state which is also the initial state.
-#define STA_TACOS_NUM_STATES 1
+#define STA_TACOS_NUM_STATES 3
#define STA_TACOS_INITIAL_STATE 0
#endif /* INC_STA_CONFIG_HPP_ */
diff --git a/App/Inc/tasks/disturb.hpp b/App/Inc/tasks/disturb.hpp
new file mode 100644
index 0000000..4919fee
--- /dev/null
+++ b/App/Inc/tasks/disturb.hpp
@@ -0,0 +1,26 @@
+/*
+ * disturb.hpp
+ *
+ * Created on: Nov 20, 2023
+ * Author: Dario
+ */
+
+#ifndef INC_TASKS_DISTURB_HPP_
+#define INC_TASKS_DISTURB_HPP_
+
+#include
+
+namespace demo
+{
+ class DisturbTask : public sta::tacos::TacosThread {
+ public:
+ DisturbTask();
+
+ void init() override;
+
+ void func() override;
+ };
+} // namespace demo
+
+
+#endif /* INC_TASKS_DISTURB_HPP_ */
diff --git a/App/Inc/tasks/dummy.hpp b/App/Inc/tasks/dummy.hpp
index 0c7de2b..aea9df6 100644
--- a/App/Inc/tasks/dummy.hpp
+++ b/App/Inc/tasks/dummy.hpp
@@ -15,7 +15,6 @@ namespace demo
class DummyTask : public sta::tacos::TacosThread {
public:
DummyTask(const char* name);
- ~DummyTask() override;
void init() override;
diff --git a/App/Inc/tasks/toggle.hpp b/App/Inc/tasks/toggle.hpp
new file mode 100644
index 0000000..3d14e2f
--- /dev/null
+++ b/App/Inc/tasks/toggle.hpp
@@ -0,0 +1,26 @@
+/*
+ * toggle.hpp
+ *
+ * Created on: Nov 10, 2023
+ * Author: Dario
+ */
+
+#ifndef INC_STA_TOGGLE_HPP_
+#define INC_STA_TOGGLE_HPP_
+
+#include
+
+namespace demo
+{
+ class Toggle : public sta::tacos::TacosThread
+ {
+ public:
+ Toggle();
+
+ void init() override;
+
+ void func() override;
+ };
+}
+
+#endif /* INC_STA_TOGGLE_HPP_ */
diff --git a/App/Src/startup.cpp b/App/Src/startup.cpp
index 884a1f3..3078ce3 100644
--- a/App/Src/startup.cpp
+++ b/App/Src/startup.cpp
@@ -12,8 +12,11 @@
#include
#include
#include
+#include
+#include
#include
#include
+#include
#include
@@ -24,38 +27,6 @@ namespace sta
{
void onStatemachineInit()
{
- Statemachine::instance()->setStateFunction([](uint16_t state) -> uint16_t {
- // ###### Implement your state transition function here! ######
-
- /**
- * This function should return some state within 0 and STA_TACOS_NUM_STATES (specified in the config.hpp).
- * Return the same state as given as the argument for no state transition. Make sure, that this function is not
- * computationally expensive.
- */
-
- return state;
- });
-
- Statemachine::instance()->setTimerFunction([](uint16_t state, uint16_t lastState, uint32_t event) {
- // ###### Set the failsafe and lockout timers here ######
-
- /**
- * This function is called after a state transition from lastState to state. Event corresponds to EventFlag enum in the Statemachine
- * and encode how the state change happened.
- *
- * Setting a timer here is fully optional and depends on your system's specifications.
- */
-
- uint32_t someFailsafeMillis = 42;
- uint16_t someNextState = STA_TACOS_INITIAL_STATE;
- uint32_t someLockoutMillis = 21;
-
- // Start the failsafe timer to force a state transition to someNextState after someFailsafeMillis milliseconds.
- Statemachine::instance()->setFailsafeTimer(someFailsafeMillis, someNextState);
-
- // Start the lockout timer to block a state transition for the first someLockoutMillis milliseconds.
- Statemachine::instance()->setLockoutTimer(someLockoutMillis);
- });
}
@@ -64,7 +35,15 @@ namespace sta
// ###### Register different threads for different states here. ######
// The dummy task runs for state 0.
- Manager::instance()->registerThread(std::make_shared("Dummy"), {0});
+ Manager::instance()->registerThread(std::make_shared("State 0"), {0});
+ Manager::instance()->registerThread(std::make_shared("State 1"), {1});
+ Manager::instance()->registerThread(std::make_shared("State 2 - FAIL"), {2});
+ Manager::instance()->registerThread(std::make_shared(), {0, 1});
+ // Manager::instance()->registerThread(std::make_shared(), {0, 1});
+
+ STA_DEBUG_PRINTF("The answer to everything is %d", 42);
+
+ STA_DEBUG_HEAP_STATS();
}
} // namespace tacos
} // namespace sta
diff --git a/App/Src/tasks/disturb.cpp b/App/Src/tasks/disturb.cpp
new file mode 100644
index 0000000..8315c96
--- /dev/null
+++ b/App/Src/tasks/disturb.cpp
@@ -0,0 +1,35 @@
+/*
+ * disturb.cpp
+ *
+ * Created on: Nov 20, 2023
+ * Author: Dario
+ */
+
+#include
+#include
+#include
+
+namespace demo
+{
+ DisturbTask::DisturbTask()
+ : sta::tacos::TacosThread("Disturb", osPriorityNormal)
+ {
+
+ }
+
+ void DisturbTask::init()
+ {
+
+ }
+
+ void DisturbTask::func()
+ {
+ STA_DEBUG_PRINTLN(this->getName());
+
+ sta::tacos::Statemachine::instance()->stateChangeEvent.wait(sta::tacos::EventFlags::ALL, osWaitForever);
+
+ uint16_t currentState = sta::tacos::Statemachine::instance()->getCurrentState();
+ sta::tacos::Statemachine::instance()->requestTimedStateTransition(currentState, 2, 4000, 0);
+ }
+} // namespace demo
+
diff --git a/App/Src/tasks/dummy.cpp b/App/Src/tasks/dummy.cpp
index 5046319..41b0a8c 100644
--- a/App/Src/tasks/dummy.cpp
+++ b/App/Src/tasks/dummy.cpp
@@ -19,8 +19,6 @@ namespace demo
}
- DummyTask::~DummyTask(){}
-
void DummyTask::init()
{
diff --git a/App/Src/tasks/toggle.cpp b/App/Src/tasks/toggle.cpp
new file mode 100644
index 0000000..418c34c
--- /dev/null
+++ b/App/Src/tasks/toggle.cpp
@@ -0,0 +1,34 @@
+/*
+ * toggle.cpp
+ *
+ * Created on: Nov 10, 2023
+ * Author: Dario
+ */
+
+#include
+#include
+#include
+
+namespace demo
+{
+ Toggle::Toggle()
+ : sta::tacos::TacosThread{ "Toggle", osPriorityNormal }
+ {
+
+ }
+
+ void Toggle::init()
+ {
+
+ }
+
+ void Toggle::func()
+ {
+ uint16_t state = sta::tacos::Statemachine::instance()->getCurrentState();
+ uint16_t next = 1 - state;
+
+ sta::tacos::Statemachine::instance()->requestStateTransition(state, next, 5000);
+ }
+}
+
+
diff --git a/Libs/rtos2-utils b/Libs/rtos2-utils
index 0e96b2e..b1d6456 160000
--- a/Libs/rtos2-utils
+++ b/Libs/rtos2-utils
@@ -1 +1 @@
-Subproject commit 0e96b2ec29fde1e56e12fc9f0dceb80012e0caf3
+Subproject commit b1d6456dd88f881f68674824339483f69dd280f0
diff --git a/Libs/sta-core b/Libs/sta-core
index 4da1f0b..c469228 160000
--- a/Libs/sta-core
+++ b/Libs/sta-core
@@ -1 +1 @@
-Subproject commit 4da1f0bb7dd48f89e9dd5ee0ec181638894e55e2
+Subproject commit c4692287cf88e518d22078c1d284be343fc82fba
diff --git a/Tacos/include/sta/tacos/statemachine.hpp b/Tacos/include/sta/tacos/statemachine.hpp
index bf5fa4c..99b444a 100644
--- a/Tacos/include/sta/tacos/statemachine.hpp
+++ b/Tacos/include/sta/tacos/statemachine.hpp
@@ -20,6 +20,8 @@
# define STA_TACOS_INITIAL_STATE 0
#endif
+#define STA_TACOS_STATEMACHINE_QUEUE_LENGTH 4
+
// State transition happened because of
#define STA_TACOS_STATE_CHANGE_NORMAL_FLAG ( 0x1U )
#define STA_TACOS_STATE_CHANGE_FORCED_FLAG ( 0x1U << 1)
@@ -29,34 +31,41 @@
#include
+#include
#include
#include
#include
#include
#include
+#include
namespace sta
{
namespace tacos
{
- typedef std::function state_fn;
- typedef std::function timer_fn;
+ enum EventFlags
+ {
+ NORMAL = 0x1U,
+ FORCED = 0x1U << 1,
+ TIMEOUT = 0x1U << 2,
+ STARTUP = 0x1U << 3,
+ ALL = NORMAL | FORCED | TIMEOUT | STARTUP
+ };
+
+ struct StateTransition
+ {
+ uint16_t from;
+ uint16_t to;
+ EventFlags event;
+ uint32_t lockout;
+ };
class Statemachine : public TacosThread
{
public:
- enum EventFlags
- {
- NORMAL = 0x1U,
- FORCED = 0x1U << 1,
- TIMEOUT = 0x1U << 2,
- STARTUP = 0x1U << 3,
- ALL = NORMAL | FORCED | TIMEOUT | STARTUP
- };
-
/**
* @brief The global event signaling a state change.
*/
@@ -84,29 +93,23 @@ namespace sta
uint16_t getCurrentState() const;
/**
- * @brief Registers a new state transition function. This is a function for the user to implement state transition rules.
+ * @brief Request a state transition from a state to another.
+ *
+ * @param from The state which we want to leave. This is used to filter out obsolete transitions.
+ * @param to The state to transition to.
+ * @param lockout The minimum number of milliseconds we expect to stay in this state. This is used to block premature transitions.
*/
- void setStateFunction(state_fn func);
+ void requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout = 0);
/**
- * @brief Registers a new timer function. This is a function for the user to set the timers after a state change.
+ * @brief Request a state transition after a given time has passed.
+ *
+ * @param from The state which we want to leave. This is used to filter out obsolete transitions.
+ * @param to The state to transition to.
+ * @param millis the number of milliseconds to wait before triggering the transition.
+ * @param lockout The minimum number of milliseconds we expect to stay in this state. This is used to block premature transitions.
*/
- void setTimerFunction(timer_fn func);
-
- /**
- * @brief Starts the failsafe timer for the desired duration and the desired next state.
- */
- void setFailsafeTimer(uint32_t millis, uint16_t nextState);
-
- /**
- * @brief Starts the lockoutTimer for the desired duration.
- */
- void setLockoutTimer(uint32_t millis);
-
- /**
- * @brief this function call allows forced state transitions from external and internal sources.
- */
- void forceStateTransition(uint16_t state);
+ void requestTimedStateTransition(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout = 0);
void init() override;
void func() override;
@@ -133,15 +136,19 @@ namespace sta
~Statemachine() {}
+ private:
+ /**
+ * @brief Starts the lockoutTimer for the desired duration.
+ */
+ void setLockoutTimer(uint32_t millis);
+
private:
uint16_t currentState_;
- uint16_t failsafeState_;
RtosTimer lockoutTimer_;
RtosTimer failsafeTimer_;
- state_fn transitionFunc_;
- timer_fn timerFunc_;
+ RtosQueue queue_;
};
} // namespace tacos
} // namespace sta
diff --git a/Tacos/src/manager.cpp b/Tacos/src/manager.cpp
index 2b511ef..468c5fb 100644
--- a/Tacos/src/manager.cpp
+++ b/Tacos/src/manager.cpp
@@ -80,7 +80,7 @@ namespace sta
void Manager::func()
{
- Statemachine::stateChangeEvent.wait(Statemachine::EventFlags::ALL, osWaitForever);
+ Statemachine::stateChangeEvent.wait(EventFlags::ALL, osWaitForever);
HeapStats_t stats;
vPortGetHeapStats(&stats);
diff --git a/Tacos/src/statemachine.cpp b/Tacos/src/statemachine.cpp
index c7daf0f..a5c4609 100644
--- a/Tacos/src/statemachine.cpp
+++ b/Tacos/src/statemachine.cpp
@@ -17,47 +17,46 @@ namespace sta
Statemachine::Statemachine()
: TacosThread{"Statemachine", STA_TACOS_STATEMACHINE_PRIORITY},
currentState_{STA_TACOS_INITIAL_STATE},
- failsafeState_{STA_TACOS_INITIAL_STATE},
lockoutTimer_{[](void *){}, nullptr},
failsafeTimer_{[](void *){}, nullptr},
- transitionFunc_{[](uint16_t) -> uint16_t { return Statemachine::instance()->getCurrentState(); }},
- timerFunc_{[](uint16_t, uint16_t, uint16_t) {}}
+ queue_{STA_TACOS_STATEMACHINE_QUEUE_LENGTH}
{
STA_ASSERT(STA_TACOS_INITIAL_STATE < STA_TACOS_NUM_STATES);
}
void Statemachine::init()
{
- timerFunc_(0, 0, EventFlags::STARTUP);
+
}
void Statemachine::func()
{
- if (!lockoutTimer_.isRunning())
+ // Wait for a message to be added to the queue.
+ StateTransition transition;
+ queue_.get(&transition, osWaitForever);
+
+ // Check if the transition isn't blocked and is legal.
+ if (!lockoutTimer_.isRunning() && transition.from == currentState_)
{
- uint16_t next = transitionFunc_(currentState_);
+ STA_ASSERT(transition.to < STA_TACOS_NUM_STATES);
- STA_ASSERT(next < STA_TACOS_NUM_STATES);
+ // Perform the transition and notify the threads. The event flags are set
+ // here in order to allow threads to react immediately.
+ currentState_ = transition.to;
+ Statemachine::stateChangeEvent.set(transition.event);
+ Statemachine::stateChangeEvent.clear(EventFlags::ALL);
- // Check if a state change is desired. Block if the lockoutTimer is still running.
- if (next != currentState_)
+ if (failsafeTimer_.isRunning())
{
- if (failsafeTimer_.isRunning())
- {
- failsafeTimer_.stop();
- }
+ failsafeTimer_.stop();
+ }
- // Call user space code to set the timers again.
- timerFunc_(next, currentState_, EventFlags::NORMAL);
-
- // Update the state and trigger the global state changed event.
- currentState_ = next;
- Statemachine::stateChangeEvent.set(EventFlags::NORMAL);
- Statemachine::stateChangeEvent.clear(EventFlags::ALL);
+ // Start the lockout timer if requested.
+ if (transition.lockout != 0)
+ {
+ setLockoutTimer(transition.lockout);
}
}
-
- osThreadYield();
}
uint16_t Statemachine::getCurrentState() const
@@ -65,28 +64,27 @@ namespace sta
return currentState_;
}
- void Statemachine::setStateFunction(state_fn func)
+ void Statemachine::requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */)
{
- STA_ASSERT(func != nullptr);
+ StateTransition transition;
+ transition.from = from;
+ transition.to = to;
+ transition.event = EventFlags::NORMAL;
+ transition.lockout = lockout;
- transitionFunc_ = func;
+ // Try to add a state transition request to the queue. Don't wait if another
+ // thread is already requesting a state change.
+ queue_.put(transition, 0);
}
- void Statemachine::setTimerFunction(timer_fn func)
+ void Statemachine::requestTimedStateTransition(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout /* = 0 */)
{
- STA_ASSERT(func != nullptr);
-
- timerFunc_ = func;
- }
-
- void Statemachine::setFailsafeTimer(uint32_t millis, uint16_t nextState)
- {
- STA_ASSERT(nextState < STA_TACOS_NUM_STATES);
+ STA_ASSERT(to < STA_TACOS_NUM_STATES);
STA_ASSERT(!failsafeTimer_.isRunning());
- failsafeTimer_.setCallback([nextState](void* arg) {
- Statemachine::instance()->forceStateTransition(nextState);
- }, &failsafeState_);
+ failsafeTimer_.setCallback([from, to, lockout](void* arg) {
+ Statemachine::instance()->requestStateTransition(from, to, lockout);
+ }, NULL);
failsafeTimer_.start(millis);
}
@@ -99,25 +97,6 @@ namespace sta
lockoutTimer_.start(millis);
}
- void Statemachine::forceStateTransition(uint16_t state)
- {
- if (failsafeTimer_.isRunning())
- {
- failsafeTimer_.stop();
- }
-
- if (lockoutTimer_.isRunning())
- {
- lockoutTimer_.stop();
- }
-
- timerFunc_(state, currentState_, EventFlags::FORCED);
- currentState_ = state;
-
- Statemachine::stateChangeEvent.set(EventFlags::FORCED);
- Statemachine::stateChangeEvent.clear(EventFlags::ALL);
- }
-
Statemachine* Statemachine::_instance = nullptr;
RtosEvent Statemachine::stateChangeEvent;
diff --git a/Tacos/src/thread.cpp b/Tacos/src/thread.cpp
index d6a1226..49c6b14 100644
--- a/Tacos/src/thread.cpp
+++ b/Tacos/src/thread.cpp
@@ -81,21 +81,26 @@ namespace sta
// Infinite loop allowing quick restarts of this thread after termination.
while (true)
{
- // The thread has to wait until the startup has been completed.
+ // The thread has to wait until the system startup has been completed.
rtos::waitForStartupEvent();
// Wait for a thread start flag.
wait(STA_RTOS_THREAD_FLAG_START);
+ // Call user-defined initialization code.
running_ = true;
init();
- // Run the thread until the termination flag is set.
- while (!resetTerminationRequest())
+ // Run the thread until the termination flag is set. Reset this
+ while (!isTerminationRequested())
{
func();
}
+ // Clear the termination request flag for this thread.
+ deleteTerminationRequest();
+
+ // Call user-defined cleanup code.
cleanup();
running_ = false;
}