first chapter done
This commit is contained in:
parent
11dc19b6e3
commit
0513123532
2
fsfw
2
fsfw
@ -1 +1 @@
|
||||
Subproject commit 715d5ac39f0ea23e5edc28c653fac8b4561f5526
|
||||
Subproject commit 62f638a3d2585e4a23dea2d5a72c4f3de55467b8
|
@ -160,7 +160,7 @@ int main() {
|
||||
|
||||
Where the three tasks do their tasks with different frequencies.
|
||||
|
||||
## 3. Using the framework abstractions
|
||||
## 4. Using the framework abstractions
|
||||
|
||||
We now use framework components to perform the tasks shown above. The framework
|
||||
exposes an abstractions for executable tasks called [`ExecutableObjectIF`](https://documentation.irs.uni-stuttgart.de/fsfw/development/api/task.html).
|
||||
@ -184,14 +184,51 @@ In summary, task abstractions have the following advantages:
|
||||
### Subtasks
|
||||
|
||||
1. Load the required interfaces:
|
||||
- `#include "fsfw/tasks/ExecutableObjectIF"`
|
||||
- `#include "fsfw/tasks/PeriodicTaskIF`
|
||||
- `#include "fsfw/tasks/TaskFactory`
|
||||
- `#include "fsfw/tasks/ExecutableObjectIF.h"`
|
||||
- `#include "fsfw/tasks/PeriodicTaskIF.h`
|
||||
- `#include "fsfw/tasks/TaskFactory.h`
|
||||
2. For your three custom objects, implement the executable object IF provided by the framework
|
||||
instead of your custom interface
|
||||
3. Create two periodic tasks using the `TaskFactory::createPeriodicTask` function
|
||||
instead of your custom interface.
|
||||
3. In your main function, create an instance of the `TaskFactory`. `TaskFactory`
|
||||
is implemented as a singleton: The object will create itself when using
|
||||
`TaskFactory::instance`. All subsequent calls will return the same intance.
|
||||
There are other similar singletons to create other objects like mutexes or message queues.
|
||||
3. Create two periodic tasks using the `TaskFactory::createPeriodicTask` function.
|
||||
Some notes on the expected arguments:
|
||||
- Each task has a name. This is useful for debugging, especially because the framework
|
||||
abstractons can detect missed deadlines.
|
||||
- The task priority parameter is OS dependent. This parameter is currently ignored for the
|
||||
Linux OSAL so you can pass 0 here.
|
||||
On Windows, you can retrieve the priority by using `tasks::makeWinPriority`
|
||||
which can be loaded by including `#include "fsfw/osal/windows/winTaskHelpers.h"`
|
||||
- The stack space parameter is generally ignored or unimportant for host systems.
|
||||
You can simply pass `PeriodicTaskIF::MINIMUM_STACK_SIZE` here. This parameter becomes
|
||||
important on resource constrained OSes and systems, for example FreeRTOS.
|
||||
- The frequency is expected as floating point seconds
|
||||
- You can pass a function which will be called when a deadline is missed. This is the case
|
||||
when a tasks took longer than its designated slot frequency. This is useful to detect
|
||||
bugs in the software or generally detect when tasks require a large amount of time.
|
||||
You can also pass `nullptr` here if you do not want any function to be called.
|
||||
4. Add the first two of your custom exec objects to the first periodic task. Those tasks
|
||||
will be executed in the same thread consecutively
|
||||
will be executed in the same thread consecutively. You can use the `PeriodicTaskIF::addComponent`
|
||||
method to do this.
|
||||
5. Add the third custom exec object to the second periodic task. The third object
|
||||
gets an own thread
|
||||
6. Start both periodic tasks
|
||||
6. Start both periodic tasks and add a permanent loop at the end of the main
|
||||
method which puts the main thread into sleep.
|
||||
|
||||
You successfully scheduled some objects using the framework!
|
||||
The general concept of executble objects is used heavily throughout the framework.
|
||||
For example, each device handler or controller is an executable object, as the bases
|
||||
classes exposed by the framework implement `ExecutableObjectIF`.
|
||||
|
||||
There is also another type of periodic task handler called `FixedTimeslotTask`. Here,
|
||||
you can explicitely specify (multiple) execution slots with a specified relative time within
|
||||
the execution slot. This is useful for objects where there are multiple processing steps
|
||||
but the steps take different amount of times.
|
||||
|
||||
In examples or other OBSW implementations using the framework, you will often
|
||||
see the distrinction between an `ObjectFactory.cpp` and an `InitMission.cpp`.
|
||||
In the first file, all global (executable) objects will be created. In the second file,
|
||||
all of these objects will be scheduled. Another chapter will introduce the Object Manager
|
||||
to show what exactly is happening here.
|
||||
|
@ -1,6 +1,10 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tasks/PeriodicTaskIF.h"
|
||||
#include "fsfw/tasks/TaskFactory.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class MyExecutableObjectIF {
|
||||
@ -9,32 +13,35 @@ public:
|
||||
virtual void performOperation() = 0;
|
||||
};
|
||||
|
||||
class MyExecutableObject0: public MyExecutableObjectIF {
|
||||
class MyExecutableObject0: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject0() = default;
|
||||
|
||||
void performOperation() override {
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 0" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
class MyExecutableObject1: public MyExecutableObjectIF {
|
||||
class MyExecutableObject1: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject1() = default;
|
||||
|
||||
void performOperation() override {
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 1" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
class MyExecutableObject2: public MyExecutableObjectIF {
|
||||
class MyExecutableObject2: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject2() = default;
|
||||
|
||||
void performOperation() override {
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 2" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
@ -64,14 +71,15 @@ int main() {
|
||||
MyExecutableObject0 myExecutableObject0;
|
||||
MyExecutableObject1 myExecutableObject1;
|
||||
MyExecutableObject2 myExecutableObject2;
|
||||
MyPeriodicTask task0(myExecutableObject0, 1000);
|
||||
MyPeriodicTask task1(myExecutableObject1, 2000);
|
||||
MyPeriodicTask task2(myExecutableObject2, 5000);
|
||||
auto thread0 = task0.start();
|
||||
auto thread1 = task1.start();
|
||||
auto thread2 = task2.start();
|
||||
thread0.join();
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
return 0;
|
||||
auto* factory = TaskFactory::instance();
|
||||
auto* periodicTask0 = factory->createPeriodicTask("TASK_0", 0, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, nullptr);
|
||||
auto* periodicTask1 = factory->createPeriodicTask("TASK_1", 0, PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.0, nullptr);
|
||||
periodicTask0->addComponent(&myExecutableObject0);
|
||||
periodicTask0->addComponent(&myExecutableObject1);
|
||||
periodicTask1->addComponent(&myExecutableObject2);
|
||||
periodicTask0->startTask();
|
||||
periodicTask1->startTask();
|
||||
while(true) {
|
||||
this_thread::sleep_for(5000ms);
|
||||
}
|
||||
}
|
||||
|
85
start/tasks-srcs/main-04.cpp
Normal file
85
start/tasks-srcs/main-04.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tasks/PeriodicTaskIF.h"
|
||||
#include "fsfw/tasks/TaskFactory.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class MyExecutableObjectIF {
|
||||
public:
|
||||
virtual ~MyExecutableObjectIF() = default;
|
||||
virtual void performOperation() = 0;
|
||||
};
|
||||
|
||||
class MyExecutableObject0: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject0() = default;
|
||||
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 0" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
class MyExecutableObject1: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject1() = default;
|
||||
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 1" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
class MyExecutableObject2: public ExecutableObjectIF {
|
||||
public:
|
||||
MyExecutableObject2() = default;
|
||||
|
||||
ReturnValue_t performOperation(uint8_t opCode) override {
|
||||
cout << "Task 2" << endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
class MyPeriodicTask {
|
||||
public:
|
||||
MyPeriodicTask(MyExecutableObjectIF& executable, uint32_t taskFreqMs)
|
||||
: executable(executable), taskFreqMs(taskFreqMs) {}
|
||||
|
||||
std::thread start() {
|
||||
return std::thread(
|
||||
MyPeriodicTask::executeTask,
|
||||
std::reference_wrapper(*this));
|
||||
}
|
||||
private:
|
||||
static void executeTask(MyPeriodicTask& self) {
|
||||
while(true) {
|
||||
self.executable.performOperation();
|
||||
this_thread::sleep_for(std::chrono::milliseconds(self.taskFreqMs));
|
||||
}
|
||||
}
|
||||
MyExecutableObjectIF& executable;
|
||||
uint32_t taskFreqMs;
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyExecutableObject0 myExecutableObject0;
|
||||
MyExecutableObject1 myExecutableObject1;
|
||||
MyExecutableObject2 myExecutableObject2;
|
||||
auto* factory = TaskFactory::instance();
|
||||
auto* periodicTask0 = factory->createPeriodicTask("TASK_0", 0, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, nullptr);
|
||||
auto* periodicTask1 = factory->createPeriodicTask("TASK_1", 0, PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.0, nullptr);
|
||||
periodicTask0->addComponent(&myExecutableObject0);
|
||||
periodicTask0->addComponent(&myExecutableObject1);
|
||||
periodicTask1->addComponent(&myExecutableObject2);
|
||||
periodicTask0->startTask();
|
||||
periodicTask1->startTask();
|
||||
while(true) {
|
||||
this_thread::sleep_for(5000ms);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user