Files
Geely/libs/Periodic.h

167 lines
6.4 KiB
C++

/*
* Periodic.h
*
* Создан: 09 окт. 2015 г.
* Автор: esaulenko
*
* Изменен: 21 ноя. 2019 г.
* Автор: gavrilov
*/
#ifndef PERIODIC_H_
#define PERIODIC_H_
#include <cstdint>
#include <cstddef>
#include "SysTimer.h"
#include "Utils.h"
template <uint nTasks>
class CPeriodic
{
private:
typedef void (* TPeriodicTask) (TCanFwMem * apMem);
public:
// добавить новую функцию для периодического вызова. Обычно выполняется один раз.
void AddTask(const uint iTask, TPeriodicTask TaskHandler = nullptr, const uint32_t msTime = 0)
{
if (iTask < nTasks)
{
Tasks[iTask].Handler = TaskHandler;
SetTimeout(iTask, msTime);
}
}
// Установить время, через которое надо будет вызвать периодическую функцию
void SetTimeout(const uint iTask, uint32_t msTime = 0)
{
if (iTask < nTasks)
{// индекс не превышает размер массива
// Если вызов происходит из <Process>, то местный таймер уже был сброшен.. и, соответственно, нужно только обновить
// значение периода.. а пересчет минимального времени, также произойдет при выходе из <Process>..
// При вызове же вне <Process>, ни местный таймер не сбрасывается, ни пересчет периода не происходит..
if (!Mutex)
{// При вызове вне <Process>:
// 1. Нужно обязательно вызывать метод <SetNextCall>, т.к. следующий период может как удлиниться, так и укоротиться.
// 2. А для того, чтобы метод <SetNextCall> правильно выставил время следующего вызова <Process>,
// требуется пересчитать все текущие периоды с учетом набега времени.
// 3. А раз нам требуется пересчитать все текущие периоды, то значит мы должны пересбросить местный
// таймер отсчета времени.
// 4. Однако, т.к. отсюда нельзя вызывать обработчики, то тем периодам, что окончились здесь,
// приходится ставить минимальное значение =1, чтобы обеспечить как можно скорее вызов обработчика <Process>.
//
auto elapsed = Timer.Restart();
if (elapsed)
for (auto & Task: Tasks)
// Пройдемся по всем задачам..
if (Task.Timeout)
{
// task enabled
if (Task.Timeout > elapsed)
Task.Timeout -= elapsed;
else
// Отсюда мы не можем вызвать обработчик, поэтому просто ставим минимальное время..
Task.Timeout = 1;
}
}
if (Tasks[iTask].Handler == nullptr)
// Если обработчик неопределен.. остановить задачу..
msTime = 0;
// Установить время..
Tasks[iTask].Timeout = msTime;
// Всегда сбрасывать признак выполненной задачи, т.к. только-что для нее был выставлен новый период..
// ..следовательно, предыдущий период уже стал неактуальным..
Tasks[iTask].Done = false;
// Пересчитать интервалы..
if (!Mutex)
// ..здесь только для вызывов извне <Process>..
SetNextCall();
}
}
// остановить задачу
void Stop(const uint iTask)
{
if (iTask < nTasks)
{
Tasks[iTask].Timeout = 0;
Tasks[iTask].Done = false;
}
}
// проверить, что задача запущена
bool isActive(const uint iTask) const
{
if (iTask < nTasks)
if (Tasks[iTask].Handler != nullptr)
if (Tasks[iTask].Timeout)
return true;
return false;
}
private:
struct {
TPeriodicTask Handler = nullptr;
uint32_t Timeout = 0;
bool Done = false;
} Tasks[nTasks];
TimerMs Timer;
volatile bool Mutex = false;
// Обработчик, вызывается из PeriodicProcess()
void Process(TCanFwMem * apCanMemory)
{
auto elapsed = Timer.Restart();
if (elapsed)
{
// Сначала нужно обновить периоды всех периодических функций..
for (auto & Task: Tasks)
// Пройдемся по всем задачам..
if (Task.Timeout)
{
// task enabled
if (Task.Timeout > elapsed)
Task.Timeout -= elapsed;
else
{
Task.Done = true;
Task.Timeout = 0;
}
}
// Затем запустить все сработавшие задачи..
Mutex = true; // выставить признак того, что метод <SetTimeout> будет вызван из метода <Process>
for (auto & Task: Tasks)
// Пройдемся по всем задачам..
if (Task.Done)
{// Если задача сработала..
Task.Done = false;
if (Task.Handler != nullptr)
// task exist
Task.Handler(apCanMemory); // ..запустить ее
}
Mutex = false;
// Запустить перерасчет и установку минимального интревала времени для вызова следующей периодической функции
SetNextCall();
}
}
friend void PeriodicProcess(TCanFwMem * vars);
// Расчитать минимальное значение интервала времени для вызова следующей периодической функции
void SetNextCall(void)
{
uint32_t minTime = UINT32_MAX;
for (const auto & Task: Tasks)
if (Task.Timeout)
if (Task.Timeout < minTime)
minTime = Task.Timeout;
if (minTime == UINT32_MAX)
minTime = 0; // Остановить вызов <PeriodicProcess>
CoreFunc->SetPeriod(minTime);
}
};
#endif /* PERIODIC_H_ */