Initial commit
This commit is contained in:
166
libs/Periodic.h
Normal file
166
libs/Periodic.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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_ */
|
||||
Reference in New Issue
Block a user