maybe fix hang on PIEthernet::interrupt() replace piLetobe with piChangeEndian: * piChangeEndianBinary * piChangeBinary * piChangedBinary PIDiagnostics::start now accept PISystemTime instead of number add PITimer::start(PISystemTime, std::function<void()>) overload
305 lines
7.6 KiB
C++
305 lines
7.6 KiB
C++
/*
|
||
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 <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#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<void(int)> func) {
|
||
initFirst();
|
||
ret_func = func;
|
||
}
|
||
|
||
|
||
PITimer::PITimer(std::function<void()> 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<void()> 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<void(int)> func) {
|
||
delims << Delimiter(func, delim);
|
||
}
|
||
|
||
|
||
void PITimer::addDelimiter(int delim, std::function<void()> 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();
|
||
}
|