first chapter done

This commit is contained in:
Robin Müller 2022-09-28 19:44:30 +02:00
parent 11dc19b6e3
commit 0513123532
No known key found for this signature in database
GPG Key ID: 11D4952C8CCEF814
4 changed files with 155 additions and 25 deletions

2
fsfw

@ -1 +1 @@
Subproject commit 715d5ac39f0ea23e5edc28c653fac8b4561f5526
Subproject commit 62f638a3d2585e4a23dea2d5a72c4f3de55467b8

View File

@ -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.

View File

@ -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);
}
}

View 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);
}
}