/* * Periodic.h * * Создан: 09 окт. 2015 г. * Автор: esaulenko * * Изменен: 21 ноя. 2019 г. * Автор: gavrilov */ #ifndef PERIODIC_H_ #define PERIODIC_H_ #include #include #include "SysTimer.h" #include "Utils.h" template 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) {// индекс не превышает размер массива // Если вызов происходит из , то местный таймер уже был сброшен.. и, соответственно, нужно только обновить // значение периода.. а пересчет минимального времени, также произойдет при выходе из .. // При вызове же вне , ни местный таймер не сбрасывается, ни пересчет периода не происходит.. if (!Mutex) {// При вызове вне : // 1. Нужно обязательно вызывать метод , т.к. следующий период может как удлиниться, так и укоротиться. // 2. А для того, чтобы метод правильно выставил время следующего вызова , // требуется пересчитать все текущие периоды с учетом набега времени. // 3. А раз нам требуется пересчитать все текущие периоды, то значит мы должны пересбросить местный // таймер отсчета времени. // 4. Однако, т.к. отсюда нельзя вызывать обработчики, то тем периодам, что окончились здесь, // приходится ставить минимальное значение =1, чтобы обеспечить как можно скорее вызов обработчика . // 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) // ..здесь только для вызывов извне .. 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; // выставить признак того, что метод будет вызван из метода 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; // Остановить вызов CoreFunc->SetPeriod(minTime); } }; #endif /* PERIODIC_H_ */