/* PIP - Platform Independent Primitives Timer Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "pitimer.h" #include "piconditionvar.h" #include "piliterals_time.h" #include "pithread.h" //! \addtogroup Thread //! \{ //! \class PITimer pitimer.h //! //! \~\brief //! \~english Timer //! \~russian Таймер //! //! \~\details //! //! \~english \section PITimer_sec0 Synopsis //! \~russian \section PITimer_sec0 Краткий обзор //! \~english //! This class implements timer function. PIP timers supports 3 way to tick notify, //! deferred start and frequency delimiters. //! //! \~russian //! Этот класс реализует таймер. Таймер PIP поддерживает 3 варианта уведомления, //! отложенный старт и делители частоты. //! //! //! \~english \section PITimer_sec1 Notify variants //! \~russian \section PITimer_sec1 Варианты уведомления //! \~english //! Notify variants: //! * "slot" - static function with format void func(int delimiter) or [lambda //! expression](https://en.cppreference.com/w/cpp/language/lambda); //! * event - \a tickEvent(); //! * virtual function - \a tick(). //! //! Lambda should be [ ]( ){ } or [ ](int){ } format. //! //! All these variants are equivalent, use most applicable. //! \~russian //! Варианты уведомления: //! * "slot" - статический метод в формате void func(int delimiter) или //! [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda); //! * event - \a tickEvent(); //! * виртуальный метод - \a tick(). //! //! Лямбда-функция должна быть в формате [ ]( ){ } или [ ](int){ }. //! //! Все варианты аналогичны друг другу, используйте самый удобный. //! //! //! \~english \section PITimer_sec2 Frequency delimiters //! \~russian \section PITimer_sec2 Делители частоты //! \~english //! Frequency delimiter is an integer number and "slot" function. If "slot" function is null //! timer main "slot" will be used. Each delimiter numbers tick timer will be execute //! delimiters or timer main "slot" function with \b delimiter value = delimiter number. //! //! Example: //! //! \~russian //! Делитель частоты это целое число и необязательный метод "slot". Если метод не указан, //! то будет использован основной "slot". Каждые \b delimiter тиков будет дополнительно вызван //! "slot" делителя либо основной "slot" с аргументом \b delimiter равным значению делителя. //! //! Пример: //! //! \~\code //! void tfunc(int delim) { //! piCout << "tick with delimiter" << delim; //! }; //! void tfunc4(int delim) { //! piCout << "tick4 with delimiter" << delim; //! }; //! int main() { //! PITimer timer(tfunc); //! timer.addDelimiter(2); //! timer.addDelimiter(4, tfunc4); //! timer.start(50); //! piMSleep(200); //! timer.stopAndWait(); //! return 0; //! }; //! /* Result: //! tick with delimiter 1 //! tick with delimiter 1 //! tick with delimiter 2 //! tick with delimiter 1 //! tick with delimiter 1 //! tick with delimiter 2 //! tick4 with delimiter 4 //! */ //! \endcode //! //! \} PITimer::PITimer(): PIObject() { initFirst(); } PITimer::PITimer(std::function func) { initFirst(); ret_func = func; } PITimer::PITimer(std::function func) { initFirst(); ret_func = [func](int) { func(); }; } PITimer::~PITimer() { stopAndWait(); piDeleteSafety(thread); } PISystemTime PITimer::interval() const { return m_interval; } void PITimer::setInterval(PISystemTime interval) { if (m_interval == interval) return; m_interval = interval; if (isRunning()) { // piCout << "change interval runtime"; stopAndWait(); start(); } } bool PITimer::isRunning() const { return thread->isRunning(); } bool PITimer::isStopping() const { return thread->isStopping(); } bool PITimer::waitForFinish(PISystemTime timeout) { return thread->waitForFinish(timeout); } void PITimer::initFirst() { thread = new PIThread([this] { threadFunc(); }); thread->setName("_S.PITimer.thread"); setProperty("interval", PISystemTime()); } void PITimer::threadFunc() { PISystemTime st_wait = m_time_next - PISystemTime::current(true); // piCout << "wait" << this << st_wait; if (st_wait.abs() > m_interval_x5 || st_wait.seconds <= -5) { // piCout << &thread_ << "adjust" << "..."; adjustTimes(); // piCout << &thread_ << "adjust" << "ok"; return; } if (thread->isStopping()) return; if (st_wait.isPositive()) { thread->mutex().lock(); event.waitFor(thread->mutex(), st_wait); thread->mutex().unlock(); } if (thread->isStopping()) return; m_time_next += m_interval; // piCout << &thread_ << "tfunc" << "..."; execTick(); } void PITimer::adjustTimes() { PISystemTime cst = PISystemTime::current(true); if (m_time_next < cst) { int rs = (cst - m_time_next).toSeconds() / m_interval.toSeconds(); if (rs >= 100) m_time_next = cst + m_interval; else { while (m_time_next < cst) m_time_next += m_interval; } } else { int rs = (m_time_next - cst).toSeconds() / m_interval.toSeconds(); if (rs >= 100) m_time_next = cst - m_interval; else { cst += m_interval; while (m_time_next > cst) m_time_next -= m_interval; } } } void PITimer::execTick() { if (!isRunning()) return; if (lockRun) lock(); if (ret_func) ret_func(1); tick(1); tickEvent(1); if (callEvents) maybeCallQueuedEvents(); for (Delimiter & i: delims) { if (i.delim > ++(i.tick)) continue; i.tick = 0; if (i.func) i.func(i.delim); else if (ret_func) ret_func(i.delim); tick(i.delim); tickEvent(i.delim); } if (lockRun) unlock(); } bool PITimer::start() { if (isRunning()) return true; m_interval_x5 = m_interval * 5; if (m_interval_x5.toSeconds() < 1.) m_interval_x5 = 1_s; m_time_next = PISystemTime::current(true) + m_interval; if (!thread->start()) return false; return thread->waitForStart(); } bool PITimer::start(PISystemTime interval) { if (isRunning()) stopAndWait(); setInterval(interval); return start(); } bool PITimer::start(PISystemTime interval, std::function func) { if (isRunning()) stopAndWait(); setInterval(interval); setSlot(func); return start(); } void PITimer::stopAndWait(PISystemTime timeout) { stop(); thread->waitForFinish(timeout); } void PITimer::addDelimiter(int delim, std::function func) { delims << Delimiter(func, delim); } void PITimer::addDelimiter(int delim, std::function func) { delims << Delimiter([func](int) { func(); }, delim); } void PITimer::removeDelimiter(int delim) { for (int i = 0; i < delims.size_s(); ++i) if (delims[i].delim == delim) { delims.remove(i); i--; } } bool PITimer::restart() { stopAndWait(); return start(); } void PITimer::stop() { thread->stop(); event.notifyAll(); }