167 lines
6.4 KiB
C++
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_ */
|