Skip to content

Application_of_Events

Olaf Maibaum edited this page Oct 9, 2020 · 1 revision

Application of Events

Events in the Tasking Framework are the means to handle time in your application. The Tasking Framework scheduler contains a clock interface to wake up tasks at a specific time. The class Event is a specialization of a channel and the clock interface of the scheduler will fire the event at its programmed time. The timer of the clock interface is relative to the start time of the scheduler, so it is local to your process and not a global clock. You can request this time by calling the method getTime of a scheduler.

The clock interface wakes up an available executor when the trigger time of an event arrives. The executors will first check the event queue of the clock interface for pending events, before they process a task. So handling of events has a higher priority than all tasks. But, handling of events can be blocked when all executors are currently busy with the processing of tasks. When this is not acceptable for your software, it is recommended to separate events and long running tasks by usage of more than one scheduler.

The simplest way to wake up a task at a specified time is to connect a task with an event and call the method trigger of an event.

#include <schedulerProvider.h>
#include <taskEvent.h>
#include "yourTask.hpp"

Tasking::SchedulerProvider<1u, Tasking::SchedulePolicyFifo> scheduler;
Tasking::Event event(scheduler);
YourTask task(scheduler);

int main(int argc, char** argv)
{
    task.configureInput(0u, event);
    
    scheduler.start();

    event.trigger(500u);

    std::string block;
    std::cin >> block;
    scheduler.terminate(true);
    return 0;
}

The parameter to the trigger call is the time to wait until the task gets activated. The default value is 0, which means the event is immediately fired. The time resolution of the available platforms is at the moment one millisecond. However, the timing depends always on the platform specific scheduler implementation and its clock interface.

When you want to restart your tasks always after a specific time, you can call event.trigger as the last statement in your task. Alternatively, you can program the time in the relative manner to restart the event when the reset operation of the task is processed.

event.setRelativeTiming(500u);

When configured with relative timing the task execution time will slip over time, because the next restart is always delayed by the waiting and processing time of your task. E.g. with an execution time of 1 ms for the task, activations will be 500, 1001, 1502, 2003 and so on. When you want a fixed call period you can configure an event in such a way that they start always at a specified offset time in base cycle. For example, to configure the start of the task always at 110 ms in a base cycle of 500 ms:

event.setPeriodicTiming(500u, 110u);

The starting time will be in this case at 110, 610, 1110, 1610 until the scheduler stops or the event is stopped by calling the method stop.

If you have many events running with the same period, you can configure one event with a periodic schedule instead of several events. This will shorten the event queue in the clock interface and speed up the handling of events.

#include <taskPeriodicSchedule.h>

Tasking::PeriodicSchedule periodicSchedule;
Tasking::PeriodicScheduleTrigger trigger1(0);
Tasking::PeriodicScheduleTrigger trigger2(100);
Tasking::PeriodicScheduleTrigger trigger3(350);

task1.configureInput(0u, trigger1);
task1.configureInput(0u, trigger2);
task1.configureInput(0u, trigger3);
periodicSchedule.add(trigger1);
periodicSchedule.add(trigger2);
periodicSchedule.add(trigger3);
event.setPeriodicSchedule(500, 110, periodicSchedule);

The event will now fire at 110, 210, 460, 610, 710, 960 and will fire the corresponding triggers in the periodic schedule to activate its connected tasks.

The class Event provides two abstract methods called by an executor when the event is handled. This can be used for example to poll an interface by a specialization of Event.

class UartPoll: public Tasking::Event
{
public:	
    UartPoll(Tasking::Scheduler& sched, Uart& uart): Event(sched), device(uart)
    {
    }

    bool shallFire(void) override
    {
        bool result = device.isDataAvailable();
        if(!result)
        {
            reset();
        }
        return result;
    }

    void onFire(void) override
    {
        device.read(data, dataBufferSize);
        Event::onFire();
    }

    static const size_t dataBufferSize = 512;
    char data[dataBufferSize];

protected:
    Uart& device;
};

The overridden method shallFire checks if new data is available at the UART device. If not, the event is retriggered by the reset operation. If data is available, the executor continues the handling of the event and calls the overridden method onFire, which reads out the data from the device.

Clone this wiki locally