mirror of
https://git.intern.spaceteamaachen.de/ALPAKA/TACOS.git
synced 2025-06-10 16:45:59 +00:00
Merge pull request 'Merge manager task into statemachine task' (#41) from refactor/statemachine into main
Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/TACOS/pulls/41 Reviewed-by: dario <dario@noreply.git.intern.spaceteamaachen.de>
This commit is contained in:
commit
235dd0a6d7
188
README.md
188
README.md
@ -1,18 +1,63 @@
|
||||
# TACOS
|
||||
|
||||
This is the Trajectory Analysis Control OS (TACOS) that serves as a starting point for developing TACOS-based projects, by implementing threads in the App directory. TACOS is a subset of the features provided by ALPAKA. In particular, TACOS consists of the STM32-specific components of sta-core, rtos-2 and rtos2-utils.
|
||||
This is the Trajectory Analysis Control OS (TACOS) that serves as a framework for flight computer development. TACOS offers a state machine, a CAN bus interface, a watchdog and other HAL features through it's submodules. It runs on cmsis-rtos2 FreeRTOS on STM32 microcontrollers with C++ (maybe future versions will offer external C interfaces to support a wide array of languages...).
|
||||
|
||||
## Setting Up TACOS
|
||||
To use TACOS one should implement threads, which fulfill the various roles of the module in the App directory. TACOS utilizes [ALPAKA](https://git.intern.spaceteamaachen.de/ALPAKA) features, in particular requiring [sta-core](https://git.intern.spaceteamaachen.de/ALPAKA/sta-core) and [rtos2-utils](https://git.intern.spaceteamaachen.de/ALPAKA/rtos2-utils), as such it requires these to be in it's include path.
|
||||
|
||||
Add this as a library an existing CubeIDE project using FreeRTOS. Generally, we advise you to add it as a submodule. Make sure that you add the include paths for Tacos, i.e. sta-core and rtos2-utils, to the project with the following steps:
|
||||
## Setting up a TACOS project
|
||||
|
||||
### Setting up the project
|
||||
|
||||
First one must create a new CubeIDE project with FreeRTOS. To avoid doing that however we recommend using the [ioc-collection](https://git.intern.spaceteamaachen.de/ALPAKA/ioc-collection) to get a preconfigured IOC for the STM microcontroller you are using. From here follow the following steps:
|
||||
|
||||
1. ```Import -> General -> Import an Existing STM32CubeMX Configuration File (.ioc)```
|
||||
2. Select the .ioc file from the ioc-collection
|
||||
3. Enter the project name and location you want to save the project to
|
||||
4. Select C++ as the target language
|
||||
5. Click "Finish"
|
||||
|
||||
|
||||
### Setting up the folder structure
|
||||
|
||||
Now it is necessary to setup the dependencies and include paths for TACOS. For this first create a new folder in the project directory called `Libs`. Then create another folder in the project directory called `App` with the subfolders `Inc` and `Src`. Now also create a folder called `sta` in the `Inc` folder. Finally add the empty files `App/Inc/sta/config.hpp` and `App/Src/startup.cpp`.
|
||||
|
||||
Now your project should look like this:
|
||||
```
|
||||
Properties -> C/C++ General -> Paths and Symbols -> Includes -> GNU C -> Add...
|
||||
Properties -> C/C++ General -> Paths and Symbols -> Includes -> GNU C++ -> Add...
|
||||
Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder...
|
||||
...
|
||||
App/
|
||||
├── Inc/
|
||||
│ ├── sta/
|
||||
│ │ └── config.hpp
|
||||
├── Src/
|
||||
│ └── startup.cpp
|
||||
Libs/
|
||||
...
|
||||
```
|
||||
|
||||
Create a new thread via the project's IOC and call `startTACOS()` from this thread. If your thread is called `defaultTask`, the corresponding function `StartDefaultTask` generated in `Core/Src/freertos.c` should look like this:
|
||||
### Setting up the dependencies
|
||||
|
||||
First it is recommended to initialize a git repository in the project folder with `git init`. Then add the TACOS, sta-core and rtos2-utils repositories as submodules in the `Libs` folder with the following commands:
|
||||
```bash
|
||||
cd Libs
|
||||
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/TACOS.git
|
||||
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/sta-core.git
|
||||
git submodule add https://git.intern.spaceteamaachen.de/ALPAKA/rtos2-utils.git
|
||||
```
|
||||
|
||||
Make sure that you add the include paths for TACOS, sta-core and rtos2-utils to the project with the following steps:
|
||||
1. `Properties -> C/C++ General -> Paths and Symbols -> Includes -> GNU C -> Add...`
|
||||
2. Select `Add to all languages` and `Is a workspace path`
|
||||
3. Click on `Workspace` and select a folder from the `YOUR_PROJECT_FOLDER/(Libs|App)` directory
|
||||
- Always select the `include` or `Inc` folder for the include paths
|
||||
- If the path you want to add is not in the list, refresh the project with `F5` in the `Project Explorer` and try again
|
||||
4. Repeat for TACOS, sta-core, rtos2-utils and the App folder
|
||||
5. `Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder...`
|
||||
- Add the `App` and `Libs` folders
|
||||
|
||||
### Starting TACOS
|
||||
|
||||
Navigate to the `Core/Src/freertos.c` file and add the following code to the `StartDefaultTask` function:
|
||||
```cpp
|
||||
void StartDefaultTask(void *argument)
|
||||
{
|
||||
/* USER CODE BEGIN StartDefaultTask */
|
||||
@ -28,9 +73,11 @@ void StartDefaultTask(void *argument)
|
||||
}
|
||||
```
|
||||
|
||||
## Configuring TACOS
|
||||
This will start the TACOS startup and initialize all TACOS threads (which will then initialize yours).
|
||||
|
||||
### Configuring TACOS
|
||||
In order to use TACOS, you need to provide a configuration file in the path `sta/config.hpp`. The following code is an example for a TACOS-project using default configuration:
|
||||
```
|
||||
```cpp
|
||||
#ifndef INC_STA_CONFIG_HPP_
|
||||
#define INC_STA_CONFIG_HPP_
|
||||
|
||||
@ -50,6 +97,7 @@ In order to use TACOS, you need to provide a configuration file in the path `sta
|
||||
#define STA_RTOS_SYSTEM_EVENTS_ENABLE
|
||||
// #define STA_RTOS_SYSTEM_WATCHDOG_ENABLE
|
||||
// #define STA_RTOS_WATCHDOG_ENABLE
|
||||
// #define STA_TACOS_WATCHDOG_FREQUENCY 10000
|
||||
#define STA_CAN_BUS_ENABLE
|
||||
|
||||
// Statemachine settings.
|
||||
@ -61,12 +109,85 @@ In order to use TACOS, you need to provide a configuration file in the path `sta
|
||||
#endif /* INC_STA_CONFIG_HPP_ */
|
||||
```
|
||||
PS: For not officially supported chips use this as the include:
|
||||
```
|
||||
```cpp
|
||||
#include <sta/devices/stm32/mcu/common.hpp>
|
||||
#define STA_MCU_LITTLE_ENDIAN
|
||||
#define STA_PLATFORM_STM32
|
||||
```
|
||||
## Setting up the CAN Bus
|
||||
|
||||
### Implementing your own threads
|
||||
|
||||
Let's create a simple thread that prints "Hello World" every second. First create a new file in the `App/Inc/tasks` folder called `spam_task.hpp`. Then add the following code:
|
||||
```cpp
|
||||
#ifndef INC_TASKS_SPAM_TASK_HPP_
|
||||
#define INC_TASKS_SPAM_TASK_HPP_
|
||||
|
||||
#include <sta/tacos.hpp>
|
||||
|
||||
namespace tasks
|
||||
{
|
||||
class SpamTask : public sta::tacos::TacosThread {
|
||||
public:
|
||||
SpamTask();
|
||||
|
||||
// One time function that is called when the thread is created.
|
||||
void init() override;
|
||||
|
||||
// Repeatable function that is called every time the thread is executed.
|
||||
void func() override;
|
||||
};
|
||||
} // namespace tasks
|
||||
|
||||
#endif /* INC_TASKS_SPAM_TASK_HPP_ */
|
||||
```
|
||||
This code defines a new thread that inherits from `TacosThread` and implements the `init` and `func` functions. The `init` function is called once when the thread is created and the `func` function is called every time the thread is executed.
|
||||
|
||||
Now create a new file in the `App/Src/tasks` folder called `spam_task.cpp` and add the following code:
|
||||
```cpp
|
||||
#include <tasks/spam_task.hpp>
|
||||
|
||||
namespace tasks {
|
||||
SpamTask::SpamTask() :
|
||||
TacosThread("SPAM", osPriorityNormal){}
|
||||
|
||||
void SpamTask::init() {
|
||||
// Nothing to init...
|
||||
}
|
||||
|
||||
void SpamTask::func() {
|
||||
// Print "Hello World" every second.
|
||||
STA_DEBUG_PRINTLN("Hello World");
|
||||
this->periodicDelay(1); // Execute this function with 1 Hz.
|
||||
}
|
||||
} // namespace tasks
|
||||
```
|
||||
|
||||
To start this thread, we first need to fill out the `startup.cpp` file. This file may look like this:
|
||||
```cpp
|
||||
#include <sta/tacos.hpp>
|
||||
#include <tasks/spam_task.hpp>
|
||||
|
||||
#include <sta/debug/debug.hpp>
|
||||
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
void onStatemachineInit()
|
||||
{
|
||||
// ###### Register different threads for different states here. ######
|
||||
// Register a "Spam Task" thread for all states except 1 and 2.
|
||||
sta::tacos::addThread<tasks::SpamTask>(ALL_STATES - state_set{1,2});
|
||||
|
||||
STA_DEBUG_PRINTF("The answer to everything is %d", 42);
|
||||
}
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
||||
```
|
||||
|
||||
And that's it! Now you have a thread that prints "Hello World" every second. Simply build the project and flash it to your microcontroller and be amazed by the Spam!
|
||||
|
||||
### Setting up the CAN Bus
|
||||
|
||||
To enable the CAN Bus two things need to be done:
|
||||
1. Enable CAN in the IOC with the RX0 and RX1 Interrupts enabled.
|
||||
@ -79,4 +200,47 @@ PS: For not officially supported chips add this:
|
||||
#define STA_STM32_CAN_HANDLE {YOUR_HCAN_HANDLE}
|
||||
```
|
||||
|
||||
After this messages will automatically be forwarded to the task with it's ID. To send messages use the interface defined in `tacos.hpp`.
|
||||
There are two options for handling incoming CAN messages:
|
||||
1. If `#define STA_CAN_BUS_FWD_ENABLE` is set, the messages will be forwarded to the task with the ID of the message.
|
||||
- Tasks set their ID with `setID(uint32_t id)` in their constructor.
|
||||
- From here they can handle the message by going through their `CAN_queue_` with `CanSysMsg msg; CAN_queue_.get(&msg);`
|
||||
2. All messages will trigger the weakly defined handleSysMessage callback.
|
||||
- This could be implemented like this:
|
||||
```cpp
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
bool handleSysMessage(CanMsgHeader &header, uint8_t *payload)
|
||||
{
|
||||
// Print the message ID and the first byte of the payload.
|
||||
//(please don't do this in production, it will crash the system sooner or later)
|
||||
STA_DEBUG_PRINTF("> ID: %d", header.sid);
|
||||
|
||||
switch (header.sid)
|
||||
{
|
||||
// State transition message
|
||||
case STA_TACOS_CAN_BUS_SYS_MSG_ID:
|
||||
// First byte of payload is the origin state, second byte is the destination state
|
||||
tacos::setState(payload[0], payload[1], 0, true);
|
||||
return true;
|
||||
|
||||
case MODULE_SW_RESET_CAN_ID:
|
||||
HAL_NVIC_SystemReset();
|
||||
|
||||
return true; // :)
|
||||
|
||||
// ...
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; // I know, i know, this is not necessary, but it's good practice. And you know what they say about good practice: Do it!
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Further information
|
||||
To look into other function of TACOS please consult the READMEs in the include folder or the doxygen documentation. Also consult the sta-core and rtos2-utils READMEs for further information on the features that TACOS uses.
|
93
include/sta/README.md
Normal file
93
include/sta/README.md
Normal file
@ -0,0 +1,93 @@
|
||||
# TACOS.hpp
|
||||
|
||||
The TACOS API is defined in the tacos.hpp, in normal use cases you should only need to include this file.
|
||||
|
||||
## Functions
|
||||
|
||||
```cpp
|
||||
uint16_t getState()
|
||||
```
|
||||
|
||||
Retrieves the current state of the TACOS state machine.
|
||||
|
||||
---
|
||||
```cpp
|
||||
void requestState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true)
|
||||
```
|
||||
|
||||
Requests a state transition. Invalid state transitions will be dismissed. First come, first serve.
|
||||
|
||||
- **Parameters**:
|
||||
- `from`: The starting state for the transition.
|
||||
- `to`: The target state to transition to.
|
||||
- `lockout`: (Optional) A timer to block further transitions.
|
||||
- `publish`: (Optional) Whether to publish the state transition to the CAN bus.
|
||||
|
||||
```cpp
|
||||
void forceState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true)
|
||||
```
|
||||
|
||||
Forces a state transition. Will be ignored if already in the given state. Triggers instantly.
|
||||
|
||||
- **Parameters**:
|
||||
- `from`: The starting state for the transition.
|
||||
- `to`: The target state to transition to.
|
||||
- `lockout`: (Optional) A timer to block further transitions.
|
||||
- `publish`: (Optional) Whether to publish the state transition to the CAN bus.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void setStateTimed(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout = 0, bool publish = false)
|
||||
```
|
||||
|
||||
Requests a state transition after a specified time.
|
||||
|
||||
- **Parameters**:
|
||||
- `from`: The starting state.
|
||||
- `to`: The target state.
|
||||
- `millis`: The wait time in milliseconds before requesting the transition.
|
||||
- `lockout`: (Optional) A timer for blocking subsequent transitions.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template<typename T, typename ... Args> std::shared_ptr<T> addThread(std::set<uint16_t> states, Args ... args)
|
||||
```
|
||||
|
||||
Registers a new thread to be run by TACOS.
|
||||
|
||||
- **Template Parameters**:
|
||||
- `T`: The class type of the thread, which should inherit from `TacosThread`.
|
||||
- **Parameters**:
|
||||
- `states`: A set of states in which the thread should be active.
|
||||
- `args`: The constructor arguments for the thread class.
|
||||
|
||||
---
|
||||
|
||||
### CAN Bus Functions (Conditional)
|
||||
|
||||
The following functions are available only if `STA_TACOS_CAN_BUS_ENABLED` is defined:
|
||||
|
||||
```cpp
|
||||
bool queueCanBusMsg(CanSysMsg & msg, uint32_t timeout)
|
||||
```
|
||||
|
||||
Queues a message to be sent over the CAN bus.
|
||||
|
||||
- **Parameters**:
|
||||
- `msg`: The message to be sent.
|
||||
- `timeout`: The maximum time to wait for sending the message.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool publishState(uint32_t from, uint32_t to, uint32_t timeout = 0)
|
||||
```
|
||||
|
||||
Publishes a state transition message to the CAN bus.
|
||||
|
||||
- **Parameters**:
|
||||
- `from`: The starting state for the transition.
|
||||
- `to`: The target state.
|
||||
- `timeout`: (Optional) A timeout for CAN communication.
|
@ -9,7 +9,6 @@
|
||||
#define STA_TACOS_HPP
|
||||
|
||||
#include <sta/tacos/thread.hpp>
|
||||
#include <sta/tacos/manager.hpp>
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
#include <sta/tacos/can_bus.hpp>
|
||||
|
||||
@ -42,11 +41,23 @@ namespace sta
|
||||
* @param from The start we want to transition from.
|
||||
* @param to The state we want to transition to.
|
||||
* @param lockout An optional timer blocking state transition for a given time.
|
||||
* @param force If true, the state transition will be executed regardless of the current state.
|
||||
* @param publish If true, the state transition will be published via CAN.
|
||||
*
|
||||
* @ingroup tacos_api
|
||||
*/
|
||||
void setState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool force = false, bool publish = false);
|
||||
void requestState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true);
|
||||
|
||||
/**
|
||||
* @brief Request a state transition. Invalid state transitions will be dismissed.
|
||||
*
|
||||
* @param from The start we want to transition from.
|
||||
* @param to The state we want to transition to.
|
||||
* @param lockout An optional timer blocking state transition for a given time.
|
||||
* @param publish If true, the state transition will be published via CAN.
|
||||
*
|
||||
* @ingroup tacos_api
|
||||
*/
|
||||
void forceState(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true);
|
||||
|
||||
/**
|
||||
* @brief Request a state transition after a given time has passed. Invalid state transitions will be dismissed.
|
||||
@ -74,7 +85,7 @@ namespace sta
|
||||
{
|
||||
std::shared_ptr<T> thread_ptr = std::make_shared<T>(args...);
|
||||
|
||||
Manager::instance()->registerThread(thread_ptr, states);
|
||||
Statemachine::instance()->registerThread(thread_ptr, states);
|
||||
|
||||
return thread_ptr;
|
||||
}
|
||||
|
23
include/sta/tacos/README.md
Normal file
23
include/sta/tacos/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TACOS tasks and prototypes
|
||||
|
||||
## Statemachine
|
||||
The statemachine is the core of TACOS. It is responsible for managing the state of the system and executing the threads that are registered to it. The statemachine is a singleton and can be accessed via `sta::tacos::Statemachine::instance()`.
|
||||
|
||||
For further info check the file or the doxygen documentation.
|
||||
|
||||
## Watchdog
|
||||
The watchdog checks if all threads are setting a flag after every func call. If a thread does not set the flag in the given interval the thread is restarted.
|
||||
|
||||
The watchdog is enabled and configured with the following defines:
|
||||
```cpp
|
||||
#define STA_RTOS_WATCHDOG_ENABLE
|
||||
#define STA_TACOS_WATCHDOG_FREQUENCY 10000
|
||||
```
|
||||
|
||||
## CAN Bus
|
||||
The CAN bus is used to communicate between different devices. The CAN bus task is triggered by IRQ from the transceiver or if something is inserted into it's output queue.
|
||||
|
||||
This thing is a huge steaming (but functioning) pile of shit. Further documentation is on hold until rework.
|
||||
|
||||
## Thread.hpp
|
||||
Here the TacosThread is defined. Very straight forward. Just take a look inside.
|
3
include/sta/tacos/c_api/README.md
Normal file
3
include/sta/tacos/c_api/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# TACOS C API
|
||||
|
||||
Currently the only C API is the entry point for starting up TACOS. This will be extended to provide full functionality via C calling conventions.
|
@ -2,13 +2,11 @@
|
||||
#define STA_TACOS_CONFIGS_DEFAULT_HPP
|
||||
|
||||
// Generally, we assume the TACOS threads to have the highest priorties.
|
||||
#define STA_TACOS_MANAGER_PRIORITY osPriorityHigh
|
||||
#define STA_TACOS_STATEMACHINE_PRIORITY osPriorityHigh
|
||||
#define STA_TACOS_WATCHDOG_PRIORITY osPriorityHigh
|
||||
#define STA_TACOS_CAN_BUS_PRIORITY osPriorityHigh
|
||||
|
||||
// Set the Stack size for the TACOS threads, to 0 to use the default stack size, set in the ioc
|
||||
#define STA_TACOS_MANAGER_STACK_SIZE 0
|
||||
#define STA_TACOS_STATEMACHINE_STACK_SIZE 0
|
||||
|
||||
// Per default, we assume state 0 to be the initial state.
|
||||
@ -22,7 +20,4 @@
|
||||
// State transition message define with highest CAN priority
|
||||
#define STA_TACOS_CAN_BUS_SYS_MSG_ID 0x0
|
||||
|
||||
// TACOS requires system events to start threads
|
||||
#define STA_RTOS_SYSTEM_EVENTS_ENABLE
|
||||
|
||||
#endif // STA_TACOS_CONFIGS_DEFAULT_HPP
|
||||
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* manager.hpp
|
||||
*
|
||||
* Created on: Sep 19, 2023
|
||||
* Author: Dario
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_STA_TACOS_MANAGER_HPP_
|
||||
#define INCLUDE_STA_TACOS_MANAGER_HPP_
|
||||
|
||||
|
||||
#include <sta/config.hpp>
|
||||
|
||||
#if !defined(STA_TACOS_MANAGER_PRIORITY) && !defined(DOXYGEN)
|
||||
# error "Manger task priority not specified in config.hpp"
|
||||
#else
|
||||
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <sta/tacos/thread.hpp>
|
||||
|
||||
/**
|
||||
* @defgroup tacos_manager Manager Task
|
||||
* @ingroup tacos
|
||||
* @brief Manager task for TACOS.
|
||||
*/
|
||||
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
/**
|
||||
* @brief Manager class for Tacos.
|
||||
*
|
||||
* @ingroup tacos_manager
|
||||
*/
|
||||
class Manager : public TacosThread
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Get the singleton instance of the manager.
|
||||
*
|
||||
* @ingroup tacos_manager
|
||||
*/
|
||||
static Manager* instance()
|
||||
{
|
||||
static CGuard g;
|
||||
|
||||
if (!_instance)
|
||||
{
|
||||
// Create a the manager singleton instance.
|
||||
Manager::_instance = new Manager();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a thread to be managed by the manager.
|
||||
*/
|
||||
void registerThread(std::shared_ptr<TacosThread> thread, std::set<uint16_t> states);
|
||||
|
||||
/**
|
||||
* @brief Get the Active Threads object
|
||||
*
|
||||
* @return std::vector<std::shared_ptr<TacosThread>>
|
||||
*/
|
||||
std::vector<std::shared_ptr<TacosThread>> getActiveThreads();
|
||||
|
||||
void init() override;
|
||||
|
||||
void func() override;
|
||||
private:
|
||||
static Manager* _instance;
|
||||
|
||||
class CGuard
|
||||
{
|
||||
public:
|
||||
~CGuard()
|
||||
{
|
||||
if( NULL != Manager::_instance )
|
||||
{
|
||||
delete Manager::_instance;
|
||||
Manager::_instance = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Manager();
|
||||
|
||||
Manager(const Manager&);
|
||||
|
||||
//~Manager();
|
||||
|
||||
/**
|
||||
* @brief Forces only threads of current state to run.
|
||||
*/
|
||||
void updateThreads();
|
||||
|
||||
/**
|
||||
* @brief Starts all threads which should be running in the given state. Does nothing if the state is already running.
|
||||
*/
|
||||
void startThreads(uint16_t state);
|
||||
|
||||
/**
|
||||
* @brief Stops all threads which should not be running in the given state.
|
||||
*/
|
||||
void stopThreads(uint16_t state);
|
||||
|
||||
/**
|
||||
* @brief Pointers to all threads which are managed by the manager.
|
||||
*
|
||||
* @ingroup tacos_manager
|
||||
*/
|
||||
std::vector<std::shared_ptr<TacosThread>> threads_[STA_TACOS_NUM_STATES];
|
||||
};
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
||||
|
||||
#endif // STA_TACOS_MANAGER_PRIORITY
|
||||
|
||||
#endif /* INCLUDE_STA_TACOS_MANAGER_HPP_ */
|
@ -66,9 +66,13 @@
|
||||
#include <sta/rtos/event.hpp>
|
||||
#include <sta/event.hpp>
|
||||
#include <sta/debug/assert.hpp>
|
||||
#include <sta/lang.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
@ -134,11 +138,15 @@ namespace sta
|
||||
public:
|
||||
/**
|
||||
* @brief The global event signaling a state change.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
static RtosEvent stateChangeEvent;
|
||||
|
||||
/**
|
||||
* @brief Getter function for the singleton instance.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
static Statemachine* instance()
|
||||
{
|
||||
@ -146,7 +154,7 @@ namespace sta
|
||||
|
||||
if (!_instance)
|
||||
{
|
||||
// Create the manager singleton instance.
|
||||
// Create the statemachine singleton instance.
|
||||
Statemachine::_instance = new Statemachine();
|
||||
}
|
||||
|
||||
@ -155,6 +163,8 @@ namespace sta
|
||||
|
||||
/**
|
||||
* @brief Returns the statemachine's current state.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
uint16_t getCurrentState() const;
|
||||
|
||||
@ -164,9 +174,23 @@ namespace sta
|
||||
* @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.
|
||||
* @param force If true, the state transition will be executed regardless of the current state.
|
||||
* @param publish If true, the state transition will be published via CAN.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout = 0, bool force = false, bool publish = true);
|
||||
void requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param publish If true, the state transition will be published via CAN.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void forceStateTransition(uint32_t from, uint32_t to, uint32_t lockout = 0, bool publish = true);
|
||||
|
||||
/**
|
||||
* @brief Request a state transition after a given time has passed.
|
||||
@ -175,9 +199,28 @@ namespace sta
|
||||
* @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.
|
||||
* @param publish If true, the state transition will be published via CAN.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void requestTimedStateTransition(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout = 0, bool publish = true);
|
||||
|
||||
/**
|
||||
* @brief Register a thread to be managed by the statemachine.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void registerThread(std::shared_ptr<TacosThread> thread, std::set<uint16_t> states);
|
||||
|
||||
/**
|
||||
* @brief Get the Active Threads object
|
||||
*
|
||||
* @return std::vector<std::shared_ptr<TacosThread>>
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
std::vector<std::shared_ptr<TacosThread>> getActiveThreads();
|
||||
|
||||
void init() override;
|
||||
void func() override;
|
||||
|
||||
@ -208,9 +251,32 @@ namespace sta
|
||||
* @brief Starts the lockoutTimer for the desired duration.
|
||||
*
|
||||
* @param millis The duration of the timer in milliseconds.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void setLockoutTimer(uint32_t millis);
|
||||
|
||||
/**
|
||||
* @brief Forces only threads of current state to run.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void updateThreads();
|
||||
|
||||
/**
|
||||
* @brief Starts all threads which should be running in the given state. Does nothing if the state is already running.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void startThreads(uint16_t state);
|
||||
|
||||
/**
|
||||
* @brief Stops all threads which should not be running in the given state.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
void stopThreads(uint16_t state);
|
||||
|
||||
private:
|
||||
uint16_t currentState_;
|
||||
|
||||
@ -218,7 +284,26 @@ namespace sta
|
||||
RtosTimer failsafeTimer_;
|
||||
|
||||
RtosQueue<StateTransition> queue_;
|
||||
|
||||
/**
|
||||
* @brief Pointers to all threads which are managed by the statemachine.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
std::vector<std::shared_ptr<TacosThread>> threads_[STA_TACOS_NUM_STATES];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Callback that is called when a state transition occurs.
|
||||
*
|
||||
* @param from The state we transitioned from.
|
||||
* @param to The state we transitioned to.
|
||||
* @param lockout The lockout time after the transition.
|
||||
*
|
||||
* @ingroup tacos_statemachine
|
||||
*/
|
||||
STA_WEAK
|
||||
void onStateTransition(uint16_t from, uint16_t to, uint32_t lockout) {}
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
||||
|
||||
|
82
include/sta/tacos/system/events.hpp
Normal file
82
include/sta/tacos/system/events.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Implementation of system events.
|
||||
*/
|
||||
#ifndef STA_TACOS_SYSTEM_EVENTS_HPP
|
||||
#define STA_TACOS_SYSTEM_EVENTS_HPP
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup STA_RTOS_SysEvent System Events
|
||||
* @ingroup STA_RTOS_API
|
||||
* @brief System events.
|
||||
*
|
||||
* Check @ref STA_RTOS_BuildConfig for configuration options.
|
||||
*/
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
// System event flags
|
||||
//
|
||||
|
||||
/**
|
||||
* @brief Startup system event flag.
|
||||
*
|
||||
* @ingroup STA_RTOS_SysEvent
|
||||
*/
|
||||
#define STA_TACOS_SYSTEM_EVENTS_STARTUP 0x100000U
|
||||
|
||||
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
/**
|
||||
* @brief Initialize system events.
|
||||
*/
|
||||
void initSystemEvents();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Signal system events.
|
||||
*
|
||||
* @param flags System event flags
|
||||
*
|
||||
* @ingroup STA_RTOS_SysEvent
|
||||
*/
|
||||
void signalSystemEvents(uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Wait for system events.
|
||||
*
|
||||
* @param flags System event flags
|
||||
* @param options osFlagsWaitAll or osFlagsWaitAny (osFlagsNoClear always set)
|
||||
* @param timeout Wait timeout (0 = instant, osWaitForever = infinite)
|
||||
*
|
||||
* @ingroup STA_RTOS_SysEvent
|
||||
*/
|
||||
void waitForSystemEvents(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Signal startup system event.
|
||||
*
|
||||
* @ingroup STA_RTOS_SysEvent
|
||||
*/
|
||||
void signalStartupEvent();
|
||||
|
||||
/**
|
||||
* @brief Wait for startup system event.
|
||||
*
|
||||
* Blocking while waiting
|
||||
*
|
||||
* @ingroup STA_RTOS_SysEvent
|
||||
*/
|
||||
void waitForStartupEvent();
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
||||
|
||||
|
||||
#endif // STA_RTOS_SYSTEM_EVENTS_HPP
|
@ -79,7 +79,7 @@ namespace sta
|
||||
* @brief Create a new thread with the given name and priority.
|
||||
*
|
||||
* @param name The thread's name. This is used for debugging.
|
||||
* @param prio The thread's priority. Generally, this should be lower than the manager and statemachine priority.
|
||||
* @param prio The thread's priority. Generally, this should be lower than the statemachine priority.
|
||||
* @param stack_size The stack size for the task. The default is 0, i.e. the stack size specified in the FreeRTOS settings.
|
||||
* @param cb_size The control block size for the task. The default is 0, i.e. the size specified in the FreeRTOS settings.
|
||||
*/
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <sta/tacos/can_bus.hpp>
|
||||
#include <sta/debug/assert.hpp>
|
||||
#include <sta/tacos/manager.hpp>
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
#include <sta/tacos.hpp>
|
||||
|
||||
extern CAN_HandleTypeDef STA_STM32_CAN_HANDLE;
|
||||
@ -64,7 +64,7 @@ namespace sta
|
||||
{
|
||||
|
||||
// Append to the correct thread's queue
|
||||
for (std::shared_ptr<TacosThread> thread : Manager::instance()->getActiveThreads())
|
||||
for (std::shared_ptr<TacosThread> thread : Statemachine::instance()->getActiveThreads())
|
||||
{
|
||||
if (thread->getCanID() == sysMsg.header.sid)
|
||||
{
|
||||
@ -160,7 +160,7 @@ namespace sta
|
||||
STA_ASSERT(header.payloadLength == 2);
|
||||
|
||||
// First byte of payload is the origin state, second byte is the destination state. Transition is forced
|
||||
tacos::setState(payload[0], payload[1], 0, true);
|
||||
tacos::forceState(payload[0], payload[1], 0, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
51
src/events.cpp
Normal file
51
src/events.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <sta/tacos/system/events.hpp>
|
||||
#include <sta/rtos/event.hpp>
|
||||
|
||||
#include <sta/debug/assert.hpp>
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Event handle
|
||||
sta::RtosEvent * systemEvents = nullptr;
|
||||
}
|
||||
|
||||
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
void initSystemEvents()
|
||||
{
|
||||
if (systemEvents == nullptr)
|
||||
{
|
||||
systemEvents = new sta::RtosEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void signalSystemEvents(uint32_t flags)
|
||||
{
|
||||
STA_ASSERT_MSG(systemEvents != nullptr, "System events not initialized");
|
||||
systemEvents->set(flags);
|
||||
}
|
||||
|
||||
void waitForSystemEvents(uint32_t flags, uint32_t options, uint32_t timeout)
|
||||
{
|
||||
STA_ASSERT_MSG(systemEvents != nullptr, "System events not initialized");
|
||||
systemEvents->peek(flags, timeout);
|
||||
}
|
||||
|
||||
void signalStartupEvent()
|
||||
{
|
||||
signalSystemEvents(STA_TACOS_SYSTEM_EVENTS_STARTUP);
|
||||
}
|
||||
|
||||
void waitForStartupEvent()
|
||||
{
|
||||
waitForSystemEvents(STA_TACOS_SYSTEM_EVENTS_STARTUP, osFlagsWaitAll, osWaitForever);
|
||||
}
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
110
src/manager.cpp
110
src/manager.cpp
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* manager.cpp
|
||||
*
|
||||
* Created on: Sep 19, 2023
|
||||
* Author: Dario
|
||||
*/
|
||||
|
||||
#include <sta/tacos/manager.hpp>
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
#include <sta/debug/debug.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <sta/tacos.hpp>
|
||||
|
||||
|
||||
namespace sta
|
||||
{
|
||||
namespace tacos
|
||||
{
|
||||
void Manager::registerThread(std::shared_ptr<TacosThread> thread, std::set<uint16_t> states)
|
||||
{
|
||||
for (uint16_t state : states)
|
||||
{
|
||||
STA_ASSERT(state < STA_TACOS_NUM_STATES);
|
||||
|
||||
threads_[state].push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<TacosThread>> Manager::getActiveThreads()
|
||||
{
|
||||
return threads_[tacos::getState()];
|
||||
}
|
||||
|
||||
void Manager::startThreads(uint16_t state)
|
||||
{
|
||||
STA_ASSERT(state < STA_TACOS_NUM_STATES);
|
||||
|
||||
for (std::shared_ptr<TacosThread> thread : threads_[state])
|
||||
{
|
||||
if (!thread->isRunning())
|
||||
{
|
||||
thread->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::stopThreads(uint16_t state)
|
||||
{
|
||||
std::set<std::shared_ptr<TacosThread>> terminated;
|
||||
|
||||
for (uint16_t other = 0; other < STA_TACOS_NUM_STATES; ++other)
|
||||
{
|
||||
if (other == state)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (std::shared_ptr<TacosThread> thread : threads_[other])
|
||||
{
|
||||
// If the thread is currently running but not part of the set of threads that should be running...
|
||||
if (thread->isRunning() && terminated.count(thread) == 0 && std::count(threads_[state].begin(), threads_[state].end(), thread) == 0)
|
||||
{
|
||||
// ...politely request termination.
|
||||
thread->requestTermination();
|
||||
terminated.emplace(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::updateThreads()
|
||||
{
|
||||
uint16_t state = Statemachine::instance()->getCurrentState();
|
||||
|
||||
stopThreads(state);
|
||||
startThreads(state);
|
||||
}
|
||||
|
||||
void Manager::init()
|
||||
{
|
||||
startThreads(Statemachine::instance()->getCurrentState());
|
||||
}
|
||||
|
||||
void Manager::func()
|
||||
{
|
||||
Statemachine::stateChangeEvent.wait(EventFlags::ALL, osWaitForever);
|
||||
|
||||
HeapStats_t stats;
|
||||
vPortGetHeapStats(&stats);
|
||||
|
||||
// Start all new tasks and stop all the tasks that aren't supposed to be running.
|
||||
updateThreads();
|
||||
}
|
||||
|
||||
Manager::Manager()
|
||||
: TacosThread{"Manager", STA_TACOS_MANAGER_PRIORITY, STA_TACOS_MANAGER_STACK_SIZE},
|
||||
threads_{}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Manager* Manager::_instance = nullptr;
|
||||
|
||||
} // namespace tacos
|
||||
} // namespace sta
|
||||
|
||||
|
@ -28,12 +28,10 @@
|
||||
#include <sta/lang.hpp>
|
||||
|
||||
// rtos2-utils-specific includes.
|
||||
#include <sta/rtos/system/startup.hpp>
|
||||
#include <sta/rtos/system/events.hpp>
|
||||
#include <sta/tacos/system/events.hpp>
|
||||
|
||||
// Tacos-specific includes.
|
||||
#include <sta/tacos/c_api/startup.h>
|
||||
#include <sta/tacos/manager.hpp>
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
#include <sta/tacos/watchdog.hpp>
|
||||
#include <sta/tacos/can_bus.hpp>
|
||||
@ -85,39 +83,22 @@ namespace sta
|
||||
namespace tacos
|
||||
{
|
||||
/**
|
||||
* @brief Function that is called before the statemachine task is started. Override it to
|
||||
* adjust the statemachine to your specifications.
|
||||
* @brief Function that is called before the statemachine task is started. It serves as an entry point for the user to
|
||||
* define the threads the statemachine should run.
|
||||
*
|
||||
* @ingroup tacos_startup
|
||||
*/
|
||||
STA_WEAK
|
||||
void onStatemachineInit()
|
||||
void startup()
|
||||
{}
|
||||
|
||||
void initStatemachine()
|
||||
{
|
||||
onStatemachineInit();
|
||||
startup();
|
||||
|
||||
Statemachine::instance()->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function that is called before the manager task is started. Override it to adjust
|
||||
* the manager to your specifications.
|
||||
*
|
||||
* @ingroup tacos_startup
|
||||
*/
|
||||
STA_WEAK
|
||||
void onManagerInit()
|
||||
{}
|
||||
|
||||
void initManager()
|
||||
{
|
||||
onManagerInit();
|
||||
|
||||
Manager::instance()->start();
|
||||
}
|
||||
|
||||
#ifdef STA_TACOS_WATCHDOG_ENABLED
|
||||
STA_WEAK
|
||||
void onWatchdogInit()
|
||||
@ -158,8 +139,6 @@ namespace sta
|
||||
|
||||
tacos::initStatemachine();
|
||||
|
||||
tacos::initManager();
|
||||
|
||||
#ifdef STA_TACOS_WATCHDOG_ENABLED
|
||||
tacos::initWatchdog();
|
||||
#endif // STA_TACOS_WATCHDOG_ENABLED
|
||||
@ -178,16 +157,14 @@ void startTACOS(void * arg)
|
||||
// Initialize HAL
|
||||
sta::initHAL();
|
||||
|
||||
// Initialize RTOS system resources
|
||||
sta::rtos::initSystem();
|
||||
// Initialize RTOS system events
|
||||
sta::tacos::initSystemEvents();
|
||||
|
||||
// Call further initialization code
|
||||
sta::tacos::startupExtras(arg);
|
||||
|
||||
// Wake threads
|
||||
#ifdef STA_RTOS_SYSTEM_EVENTS_ENABLE
|
||||
sta::rtos::signalStartupEvent();
|
||||
#endif // STA_RTOS_SYSTEM_EVENTS_ENABLE
|
||||
sta::tacos::signalStartupEvent();
|
||||
|
||||
// Check if called from thread
|
||||
if (osThreadGetId() != nullptr)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
|
||||
#include <sta/debug/debug.hpp>
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
|
||||
namespace sta
|
||||
@ -20,14 +21,15 @@ namespace sta
|
||||
currentState_{STA_TACOS_INITIAL_STATE},
|
||||
lockoutTimer_{[](void *){}, nullptr},
|
||||
failsafeTimer_{[](void *){}, nullptr},
|
||||
queue_{STA_TACOS_STATEMACHINE_QUEUE_LENGTH}
|
||||
queue_{STA_TACOS_STATEMACHINE_QUEUE_LENGTH},
|
||||
threads_{}
|
||||
{
|
||||
STA_ASSERT(STA_TACOS_INITIAL_STATE < STA_TACOS_NUM_STATES);
|
||||
}
|
||||
|
||||
void Statemachine::init()
|
||||
{
|
||||
|
||||
startThreads(getCurrentState());
|
||||
}
|
||||
|
||||
void Statemachine::func()
|
||||
@ -42,8 +44,11 @@ namespace sta
|
||||
STA_ASSERT(transition.to < STA_TACOS_NUM_STATES);
|
||||
|
||||
#ifdef STA_TACOS_CAN_BUS_ENABLED
|
||||
if (transition.publish)
|
||||
{
|
||||
// Publish the state via CAN bus.
|
||||
tacos::publishState(transition.from, transition.to, 0);
|
||||
}
|
||||
#endif // STA_TACOS_CAN_BUS_ENABLED
|
||||
|
||||
// Perform the transition and notify the threads. The event flags are set
|
||||
@ -64,6 +69,16 @@ namespace sta
|
||||
{
|
||||
setLockoutTimer(transition.lockout);
|
||||
}
|
||||
|
||||
// get heap stats at the end of the state transition
|
||||
HeapStats_t stats;
|
||||
vPortGetHeapStats(&stats);
|
||||
|
||||
// Execute the user-defined callback.
|
||||
sta::tacos::onStateTransition(transition.from, transition.to, transition.lockout);
|
||||
|
||||
// Start all new tasks and stop all the tasks that aren't supposed to be running.
|
||||
updateThreads();
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +87,7 @@ namespace sta
|
||||
return currentState_;
|
||||
}
|
||||
|
||||
void Statemachine::requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool force /* = 0 */, bool publish /* = true */)
|
||||
void Statemachine::requestStateTransition(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool publish /* = true */)
|
||||
{
|
||||
StateTransition transition;
|
||||
transition.from = from;
|
||||
@ -81,17 +96,28 @@ namespace sta
|
||||
transition.lockout = lockout;
|
||||
transition.publish = publish;
|
||||
|
||||
// Force the transition if requested, but only if the requested state is different from the current one.
|
||||
if (force && transition.to != currentState_){
|
||||
// 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::forceStateTransition(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool publish /* = true */)
|
||||
{
|
||||
// Force the transition, but only if the requested state is different from the current one.
|
||||
if (to != currentState_){
|
||||
// Perform the transition and notify the threads. The event flags are set
|
||||
// here in order to allow threads to react immediately.
|
||||
currentState_ = transition.to;
|
||||
currentState_ = to;
|
||||
|
||||
#ifdef STA_TACOS_CAN_BUS_ENABLED
|
||||
tacos::publishState(transition.from, transition.to, transition.lockout);
|
||||
if (publish)
|
||||
{
|
||||
// Publish the state via CAN bus.
|
||||
tacos::publishState(from, to, 0);
|
||||
}
|
||||
#endif // STA_TACOS_CAN_BUS_ENABLED
|
||||
|
||||
Statemachine::stateChangeEvent.set(transition.event);
|
||||
Statemachine::stateChangeEvent.set(EventFlags::NORMAL);
|
||||
Statemachine::stateChangeEvent.clear(EventFlags::ALL);
|
||||
|
||||
if (failsafeTimer_.isRunning())
|
||||
@ -100,14 +126,16 @@ namespace sta
|
||||
}
|
||||
|
||||
// Start the lockout timer if requested.
|
||||
if (transition.lockout != 0)
|
||||
if (lockout != 0)
|
||||
{
|
||||
setLockoutTimer(transition.lockout);
|
||||
setLockoutTimer(lockout);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
// Execute the user-defined callback.
|
||||
sta::tacos::onStateTransition(from, to, lockout);
|
||||
|
||||
// Start all new tasks and stop all the tasks that aren't supposed to be running.
|
||||
updateThreads();
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +144,7 @@ namespace sta
|
||||
STA_ASSERT(to < STA_TACOS_NUM_STATES);
|
||||
|
||||
failsafeTimer_.setCallback([from, to, lockout, publish](void* arg) {
|
||||
Statemachine::instance()->requestStateTransition(from, to, lockout, false, publish);
|
||||
Statemachine::instance()->requestStateTransition(from, to, lockout, publish);
|
||||
}, NULL);
|
||||
|
||||
failsafeTimer_.start(millis);
|
||||
@ -130,6 +158,66 @@ namespace sta
|
||||
lockoutTimer_.start(millis);
|
||||
}
|
||||
|
||||
void Statemachine::registerThread(std::shared_ptr<TacosThread> thread, std::set<uint16_t> states)
|
||||
{
|
||||
for (uint16_t state : states)
|
||||
{
|
||||
STA_ASSERT(state < STA_TACOS_NUM_STATES);
|
||||
|
||||
threads_[state].push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<TacosThread>> Statemachine::getActiveThreads()
|
||||
{
|
||||
return threads_[tacos::getState()];
|
||||
}
|
||||
|
||||
void Statemachine::startThreads(uint16_t state)
|
||||
{
|
||||
STA_ASSERT(state < STA_TACOS_NUM_STATES);
|
||||
|
||||
for (std::shared_ptr<TacosThread> thread : threads_[state])
|
||||
{
|
||||
if (!thread->isRunning())
|
||||
{
|
||||
thread->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Statemachine::stopThreads(uint16_t state)
|
||||
{
|
||||
std::set<std::shared_ptr<TacosThread>> terminated;
|
||||
|
||||
for (uint16_t other = 0; other < STA_TACOS_NUM_STATES; ++other)
|
||||
{
|
||||
if (other == state)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (std::shared_ptr<TacosThread> thread : threads_[other])
|
||||
{
|
||||
// If the thread is currently running but not part of the set of threads that should be running...
|
||||
if (thread->isRunning() && terminated.count(thread) == 0 && std::count(threads_[state].begin(), threads_[state].end(), thread) == 0)
|
||||
{
|
||||
// ...politely request termination.
|
||||
thread->requestTermination();
|
||||
terminated.emplace(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Statemachine::updateThreads()
|
||||
{
|
||||
uint16_t state = getCurrentState();
|
||||
|
||||
stopThreads(state);
|
||||
startThreads(state);
|
||||
}
|
||||
|
||||
Statemachine* Statemachine::_instance = nullptr;
|
||||
|
||||
RtosEvent Statemachine::stateChangeEvent;
|
||||
|
@ -16,9 +16,14 @@ namespace sta
|
||||
return Statemachine::instance()->getCurrentState();
|
||||
}
|
||||
|
||||
void setState(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool force /* = false */, bool publish /* = false */)
|
||||
void requestState(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool publish /* = true */)
|
||||
{
|
||||
Statemachine::instance()->requestStateTransition(from, to, lockout, force, publish);
|
||||
Statemachine::instance()->requestStateTransition(from, to, lockout, publish);
|
||||
}
|
||||
|
||||
void forceState(uint32_t from, uint32_t to, uint32_t lockout /* = 0 */, bool publish /* = true */)
|
||||
{
|
||||
Statemachine::instance()->forceStateTransition(from, to, lockout, publish);
|
||||
}
|
||||
|
||||
void setStateTimed(uint32_t from, uint32_t to, uint32_t millis, uint32_t lockout /* = 0 */, bool publish /* = false */)
|
||||
|
@ -7,9 +7,10 @@
|
||||
|
||||
|
||||
#include <sta/tacos/thread.hpp>
|
||||
#include <sta/tacos/system/events.hpp>
|
||||
|
||||
#include <sta/debug/assert.hpp>
|
||||
#include <sta/debug/debug.hpp>
|
||||
#include <sta/rtos/system/events.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
@ -63,7 +64,7 @@ namespace sta
|
||||
while (true)
|
||||
{
|
||||
// The thread has to wait until the system startup has been completed.
|
||||
rtos::waitForStartupEvent();
|
||||
sta::tacos::waitForStartupEvent();
|
||||
|
||||
// Wait for a thread start flag.
|
||||
wait(STA_RTOS_THREAD_FLAG_START);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#ifdef STA_TACOS_WATCHDOG_ENABLED
|
||||
|
||||
#include <sta/tacos/manager.hpp>
|
||||
#include <sta/tacos/statemachine.hpp>
|
||||
|
||||
namespace sta
|
||||
{
|
||||
@ -10,7 +10,7 @@ namespace sta
|
||||
{
|
||||
void Watchdog::func()
|
||||
{
|
||||
for (std::shared_ptr<TacosThread> thread : Manager::instance()->getActiveThreads())
|
||||
for (std::shared_ptr<TacosThread> thread : Statemachine::instance()->getActiveThreads())
|
||||
{
|
||||
switch (thread->getStatus())
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user