/* PIP - Platform Independent Primitives Timer Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "pitimer.h" #include "pisystemtests.h" bool operator ==(const PITime & t0, const PITime & t1) { return (t0.hours == t1.hours && t0.minutes == t1.minutes && t0.seconds == t1.seconds); } bool operator <(const PITime & t0, const PITime & t1) { if (t0.hours == t1.hours) { if (t0.minutes == t1.minutes) { return t0.seconds < t1.seconds; } else return t0.minutes < t1.minutes; } else return t0.hours < t1.hours; } bool operator >(const PITime & t0, const PITime & t1) { if (t0.hours == t1.hours) { if (t0.minutes == t1.minutes) { return t0.seconds > t1.seconds; } else return t0.minutes > t1.minutes; } else return t0.hours > t1.hours; } bool operator ==(const PIDate & t0, const PIDate & t1) { return (t0.year == t1.year && t0.month == t1.month && t0.day == t1.day); } bool operator <(const PIDate & t0, const PIDate & t1) { if (t0.year == t1.year) { if (t0.month == t1.month) { return t0.day < t1.day; } else return t0.month < t1.month; } else return t0.year < t1.year; } bool operator >(const PIDate & t0, const PIDate & t1) { if (t0.year == t1.year) { if (t0.month == t1.month) { return t0.day > t1.day; } else return t0.month > t1.month; } else return t0.year > t1.year; } bool operator ==(const PIDateTime & t0, const PIDateTime & t1) { return (t0.year == t1.year && t0.month == t1.month && t0.day == t1.day && t0.hours == t1.hours && t0.minutes == t1.minutes && t0.seconds == t1.seconds); } bool operator <(const PIDateTime & t0, const PIDateTime & t1) { if (t0.year == t1.year) { if (t0.month == t1.month) { if (t0.day == t1.day) { if (t0.hours == t1.hours) { if (t0.minutes == t1.minutes) { return t0.seconds < t1.seconds; } else return t0.minutes < t1.minutes; } else return t0.hours < t1.hours; } else return t0.day < t1.day; } else return t0.month < t1.month; } else return t0.year < t1.year; } bool operator >(const PIDateTime & t0, const PIDateTime & t1) { if (t0.year == t1.year) { if (t0.month == t1.month) { if (t0.day == t1.day) { if (t0.hours == t1.hours) { if (t0.minutes == t1.minutes) { return t0.seconds > t1.seconds; } else return t0.minutes > t1.minutes; } else return t0.hours > t1.hours; } else return t0.day > t1.day; } else return t0.month > t1.month; } else return t0.year > t1.year; } #ifdef PIP_TIMER_RT PITimer::TimerPool * pool = 0; #endif PITimer::PITimer(TimerEvent slot, void * data_, bool threaded_) #ifndef PIP_TIMER_RT : PIThread() { #else : PIObject() { #endif ret_func = slot; data = data_; running_ = false; interval_ = 0.; #ifdef PIP_TIMER_RT piMonitor.timers++; ti = -1; threaded = threaded_; memset(&se, 0, sizeof(se)); se.sigev_notify = SIGEV_THREAD; se.sigev_value.sival_ptr = this; se.sigev_notify_function = PITimer::timer_event; se.sigev_notify_attributes = 0; lockRun = false; #else deferred_ = false; #endif reset(); } PITimer::PITimer(bool threaded_) #ifndef PIP_TIMER_RT : PIThread() { #else : PIObject() { #endif ret_func = 0; data = 0; running_ = false; interval_ = 0.; #ifdef PIP_TIMER_RT piMonitor.timers++; ti = -1; threaded = threaded_; memset(&se, 0, sizeof(se)); se.sigev_notify = SIGEV_THREAD; se.sigev_value.sival_ptr = this; se.sigev_notify_function = PITimer::timer_event; se.sigev_notify_attributes = 0; lockRun = false; #else deferred_ = false; #endif reset(); } PITimer::~PITimer() { #ifdef PIP_TIMER_RT piMonitor.timers--; #endif stop(); } #ifdef PIP_TIMER_RT void PITimer::start(double msecs) { if (ti != -1 || msecs < 0 || running_) return; interval_ = msecs; if (!threaded) { ticks = int(msecs); if (pool == 0) pool = new TimerPool(); pool->add(this); //cout << "not threaded timer start " << msecs << " msecs\n"; if (!pool->isRunning()) pool->start(); running_ = true; return; } spec.it_interval.tv_nsec = ((int)(msecs * 1000) % 1000000) * 1000; spec.it_interval.tv_sec = (time_t)(msecs / 1000); spec.it_value = spec.it_interval; ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer " << msecs << " msecs\n"; if (ti == -1) { piCoutObj << "[PITimer] Can`t create timer for " << msecs << " msecs: " << errorString(); return; } timer_settime(timer, 0, &spec, 0); running_ = true; } void PITimer::deferredStart(double interval_msecs, double delay_msecs) { if (ti != -1 || interval_msecs < 0 || running_) return; interval_ = interval_msecs; spec.it_interval.tv_nsec = ((int)(interval_msecs * 1000) % 1000000) * 1000; spec.it_interval.tv_sec = (time_t)(interval_msecs / 1000); spec.it_value.tv_nsec = ((int)(delay_msecs * 1000) % 1000000) * 1000; spec.it_value.tv_sec = (time_t)(delay_msecs / 1000); ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer\n"; if (ti == -1) { piCoutObj << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); return; } timer_settime(timer, 0, &spec, 0); running_ = true; } void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_datetime) { if (ti != -1 || interval_msecs < 0 || running_) return; interval_ = interval_msecs; spec.it_interval.tv_nsec = ((int)(interval_msecs * 1000) % 1000000) * 1000; spec.it_interval.tv_sec = (time_t)(interval_msecs / 1000); struct tm dtm; memset(&dtm, 0, sizeof(dtm)); dtm.tm_sec = start_datetime.seconds; dtm.tm_min = start_datetime.minutes; dtm.tm_hour = start_datetime.hours; dtm.tm_mday = start_datetime.day; dtm.tm_mon = start_datetime.month - 1; dtm.tm_year = start_datetime.year - 1900; spec.it_value.tv_nsec = 0; spec.it_value.tv_sec = mktime(&dtm); ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer\n"; if (ti == -1) { piCoutObj << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); return; } timer_settime(timer, TIMER_ABSTIME, &spec, 0); running_ = true; } void PITimer::TimerPool::remove(PITimer * t) { mutex.lock(); for (int i = 0; i < timers.size_s(); ++i) if (timers[i].first == t) { timers.remove(i); mutex.unlock(); return; } mutex.unlock(); } void PITimer::TimerPool::begin() { //cout << "pool begin\n"; /*struct sigaction sa; sa.sa_flags = 0; sa.sa_handler = empty_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, 0) == -1) { piCoutObj << "[PITimer] sigaction error: " << errorString(); stop(); return; }*/ sigemptyset(&ss); sigaddset(&ss, SIGALRM); memset(&se, 0, sizeof(se)); se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGALRM; spec.it_interval.tv_nsec = 1000000; spec.it_interval.tv_sec = 0; spec.it_value = spec.it_interval; //cout << "***create pool timer\n"; if (timer_create(CLOCK_REALTIME, &se, &timer) == -1) { piCoutObj << "[PITimer] Can`t create timer for pool: " << errorString(); stop(); return; } if (timer_settime(timer, 0, &spec, 0) == -1) { piCoutObj << "[PITimer] Can`t set timer for pool: " << errorString(); stop(); return; } ti = 1; } void PITimer::TimerPool::run() { //cout << "wait ...\n"; sigwait(&ss, &si); //cout << "ok\n"; mutex.lock(); //cout << "* pool tick , pool = " << this <<", timers = " << timers.size()<<"\n"; for (int i = 0; i < timers.size_s(); ++i) { TimerPair & ct(timers[i]); sv.sival_ptr = ct.first; ct.second++; //cout << "** pool tick for " << ct.first << ", cnt " << ct.second << ", " << ct.first->ticks << "\n"; if (ct.second >= ct.first->ticks) { //cout << "*** timer "<remove(this); if (pool->isEmpty()) pool->terminate(); } } if (ti != -1) timer_delete(timer); ti = -1; interval_ = 0.; } void PITimer::timer_event(sigval e) { PITimer * ct = (PITimer * )e.sival_ptr; if (!ct->running_) return; if (ct->lockRun) ct->lock(); if (ct->ret_func != 0) ct->ret_func(ct->data, 1); ct->timeout(ct->data, 1); ct->tick(ct->data, 1); piForeach (TimerSlot & i, ct->ret_funcs) { if (i.delim > ++(i.tick)) continue; i.tick = 0; if (i.slot != 0) i.slot(ct->data, i.delim); else if (ct->ret_func != 0) ct->ret_func(ct->data, i.delim); ct->timeout(ct->data, i.delim); ct->tick(ct->data, i.delim); } if (ct->lockRun) ct->unlock(); } bool PITimer::waitForFinish(int timeout_msecs) { if (timeout_msecs < 0) { while (running_) msleep(1); return true; } int cnt = 0; while (running_ && cnt < timeout_msecs) { msleep(1); ++cnt; } return cnt < timeout_msecs; } #else void PITimer::start(double msecs) { if (msecs < 0 || running_) return; interval_ = msecs; inc_time = PISystemTime::fromMilliseconds(msecs); st_time = currentSystemTime() + inc_time; deferred_ = false; running_ = true; PIThread::start(); } void PITimer::deferredStart(double interval_msecs, double delay_msecs) { //piCoutObj << "defStart exec with" << delay_msecs << interval_msecs; if (interval_msecs < 0 || running_) return; interval_ = interval_msecs; PISystemTime cst = currentSystemTime(); inc_time = PISystemTime::fromMilliseconds(interval_msecs); st_time = currentSystemTime() + PISystemTime::fromMilliseconds(delay_msecs); if (st_time < cst) st_time = cst; running_ = deferred_ = true; PIThread::start(); //piCoutObj << "timer start def"; } void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_datetime) { //piCoutObj << "defStart exec to" << start_datetime.toString() << interval_msecs; if (interval_msecs < 0 || running_) return; interval_ = interval_msecs; PISystemTime cst = currentSystemTime(); inc_time = PISystemTime::fromMilliseconds(interval_msecs); st_time = start_datetime.toSystemTime(); if (st_time < cst) st_time = cst; running_ = deferred_ = true; PIThread::start(); //piCoutObj << "timer start def"; } void PITimer::run() { if (!running_) return; while (deferred_) { PISystemTime tst = st_time - currentSystemTime(); if (tst.seconds > 0) { piMSleep(100); if (!running_) return; continue; } if (tst.nanoseconds > 100000000) { piMSleep(100); if (!running_) return; continue; } tst.sleep(); deferred_ = false; if (!running_) return; } (st_time - currentSystemTime()).sleep(); st_time += inc_time; //if (lockRun) lock(); if (ret_func != 0) ret_func(data, 1); timeout(data, 1); tick(data, 1); piForeach (TimerSlot & i, ret_funcs) { if (i.delim > ++(i.tick)) continue; i.tick = 0; if (i.slot != 0) i.slot(data, i.delim); else if (ret_func != 0) ret_func(data, i.delim); timeout(data, i.delim); tick(data, i.delim); } //if (lockRun) unlock(); } #endif double PITimer::elapsed_n() { #ifdef WINDOWS t_cur = GetCurrentTime(); return (t_cur - t_st) * 1000000.; #else # ifdef MAC_OS clock_get_time(__pi_mac_clock, &t_cur); # else clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec - t_st.tv_sec) * 1.e+9 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns); #endif } double PITimer::elapsed_u() { #ifdef WINDOWS t_cur = GetCurrentTime(); return (t_cur - t_st) * 1000.; #else # ifdef MAC_OS clock_get_time(__pi_mac_clock, &t_cur); # else clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec - t_st.tv_sec) * 1.e+6 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+3; #endif } double PITimer::elapsed_m() { #ifdef WINDOWS t_cur = GetCurrentTime(); return (double)(t_cur - t_st); #else # ifdef MAC_OS clock_get_time(__pi_mac_clock, &t_cur); # else clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec - t_st.tv_sec) * 1.e+3 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+6; #endif } double PITimer::elapsed_s() { #ifdef WINDOWS t_cur = GetCurrentTime(); return (t_cur - t_st) / 1000.; #else # ifdef MAC_OS clock_get_time(__pi_mac_clock, &t_cur); # else clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec - t_st.tv_sec) + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+9; #endif } double PITimer::reset_time_n() { #ifdef WINDOWS return t_st * 1.e+6; #else return t_st.tv_sec * 1.e+9 + t_st.tv_nsec; #endif } double PITimer::reset_time_u() { #ifdef WINDOWS return t_st * 1.e+3; #else return t_st.tv_sec * 1.e+6 + t_st.tv_nsec / 1.e+3; #endif } double PITimer::reset_time_m() { #ifdef WINDOWS return (double)t_st; #else return t_st.tv_sec * 1.e+3 + t_st.tv_nsec / 1.e+6; #endif } double PITimer::reset_time_s() { #ifdef WINDOWS return t_st / 1000.; #else return t_st.tv_sec + t_st.tv_nsec / 1.e+9; #endif } PISystemTime PITimer::reset_time() { #ifdef WINDOWS return PISystemTime(t_st / 1000, (t_st % 1000) * 1000000); #else return PISystemTime(t_st.tv_sec, t_st.tv_nsec); #endif } double PITimer::elapsed_system_n() { #ifdef WINDOWS long t_cur = GetCurrentTime(); return (t_cur * 1000000.); #else # ifdef MAC_OS mach_timespec_t t_cur; clock_get_time(__pi_mac_clock, &t_cur); # else timespec t_cur; clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec * 1.e+9 + t_cur.tv_nsec); #endif } double PITimer::elapsed_system_u() { #ifdef WINDOWS long t_cur = GetCurrentTime(); return (t_cur * 1000.); #else # ifdef MAC_OS mach_timespec_t t_cur; clock_get_time(__pi_mac_clock, &t_cur); # else timespec t_cur; clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec * 1.e+6 + (t_cur.tv_nsec / 1.e+3)); #endif } double PITimer::elapsed_system_m() { #ifdef WINDOWS long t_cur = GetCurrentTime(); return (double)t_cur; #else # ifdef MAC_OS mach_timespec_t t_cur; clock_get_time(__pi_mac_clock, &t_cur); # else timespec t_cur; clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec * 1.e+3 + (t_cur.tv_nsec / 1.e+6)); #endif } double PITimer::elapsed_system_s() { #ifdef WINDOWS long t_cur = GetCurrentTime(); return (t_cur / 1000.); #else # ifdef MAC_OS mach_timespec_t t_cur; clock_get_time(__pi_mac_clock, &t_cur); # else timespec t_cur; clock_gettime(0, &t_cur); # endif return (t_cur.tv_sec + (t_cur.tv_nsec / 1.e+9)); #endif } PITime currentTime() { time_t rt = time(0); tm * pt = localtime(&rt); PITime t; t.seconds = pt->tm_sec; t.minutes = pt->tm_min; t.hours = pt->tm_hour; return t; } PIDate currentDate() { time_t rt = time(0); tm * pt = localtime(&rt); PIDate d; d.day = pt->tm_mday; d.month = pt->tm_mon + 1; d.year = pt->tm_year + 1900; return d; } PIDateTime currentDateTime() { time_t rt = time(0); tm * pt = localtime(&rt); PIDateTime dt; dt.milliseconds = 0; dt.seconds = pt->tm_sec; dt.minutes = pt->tm_min; dt.hours = pt->tm_hour; dt.day = pt->tm_mday; dt.month = pt->tm_mon + 1; dt.year = pt->tm_year + 1900; return dt; } PISystemTime currentSystemTime() { #ifdef WINDOWS long t_cur = GetCurrentTime(); return PISystemTime(t_cur / 1000, (t_cur % 1000) * 1000000); #else # ifdef MAC_OS mach_timespec_t t_cur; clock_get_time(__pi_mac_clock, &t_cur); # else timespec t_cur; clock_gettime(0, &t_cur); # endif return PISystemTime(t_cur.tv_sec, t_cur.tv_nsec); #endif } PIString PITime::toString(const PIString & format) const { PIString ts = format; ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0')); ts.replace("h", PIString::fromNumber(hours)); ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0')); ts.replace("m", PIString::fromNumber(minutes)); ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0')); ts.replace("s", PIString::fromNumber(seconds)); ts.replace("zzz", PIString::fromNumber(milliseconds).expandLeftTo(3, '0')); ts.replace("zz", PIString::fromNumber(milliseconds).expandLeftTo(2, '0')); ts.replace("z", PIString::fromNumber(milliseconds)); return ts; } PIString PIDate::toString(const PIString & format) const { PIString ts = format; ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0')); ts.replace("yy", PIString::fromNumber(year).right(2)); ts.replace("y", PIString::fromNumber(year).right(1)); ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0')); ts.replace("M", PIString::fromNumber(month)); ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0')); ts.replace("d", PIString::fromNumber(day)); return ts; } PIString PIDateTime::toString(const PIString & format) const { PIString ts = format; ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0')); ts.replace("yy", PIString::fromNumber(year).right(2)); ts.replace("y", PIString::fromNumber(year).right(1)); ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0')); ts.replace("M", PIString::fromNumber(month)); ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0')); ts.replace("d", PIString::fromNumber(day)); ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0')); ts.replace("h", PIString::fromNumber(hours)); ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0')); ts.replace("m", PIString::fromNumber(minutes)); ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0')); ts.replace("s", PIString::fromNumber(seconds)); ts.replace("zzz", PIString::fromNumber(milliseconds).expandLeftTo(3, '0')); ts.replace("zz", PIString::fromNumber(milliseconds).expandLeftTo(2, '0')); ts.replace("z", PIString::fromNumber(milliseconds)); return ts; } time_t PIDateTime::toSecondSinceEpoch() const { tm pt; pt.tm_sec = seconds; pt.tm_min = minutes; pt.tm_hour = hours; pt.tm_mday = day; pt.tm_mon = month - 1; pt.tm_year = year - 1900; return mktime(&pt); } PIDateTime PIDateTime::fromSecondSinceEpoch(const time_t sec) { tm * pt = localtime(&sec); PIDateTime dt; dt.seconds = pt->tm_sec; dt.minutes = pt->tm_min; dt.hours = pt->tm_hour; dt.day = pt->tm_mday; dt.month = pt->tm_mon + 1; dt.year = pt->tm_year + 1900; return dt; } PIString time2string(const PITime & time, const PIString & format) { PIString ts = format; ts.replace("hh", PIString::fromNumber(time.hours).expandLeftTo(2, '0')); ts.replace("h", PIString::fromNumber(time.hours)); ts.replace("mm", PIString::fromNumber(time.minutes).expandLeftTo(2, '0')); ts.replace("m", PIString::fromNumber(time.minutes)); ts.replace("ss", PIString::fromNumber(time.seconds).expandLeftTo(2, '0')); ts.replace("s", PIString::fromNumber(time.seconds)); return ts; } PIString date2string(const PIDate & date, const PIString & format) { PIString ts = format; ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0')); ts.replace("yy", PIString::fromNumber(date.year).right(2)); ts.replace("y", PIString::fromNumber(date.year).right(1)); ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0')); ts.replace("M", PIString::fromNumber(date.month)); ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0')); ts.replace("d", PIString::fromNumber(date.day)); return ts; } PIString datetime2string(const PIDateTime & date, const PIString & format) { PIString ts = format; ts.replace("hh", PIString::fromNumber(date.hours).expandLeftTo(2, '0')); ts.replace("h", PIString::fromNumber(date.hours)); ts.replace("mm", PIString::fromNumber(date.minutes).expandLeftTo(2, '0')); ts.replace("m", PIString::fromNumber(date.minutes)); ts.replace("ss", PIString::fromNumber(date.seconds).expandLeftTo(2, '0')); ts.replace("s", PIString::fromNumber(date.seconds)); ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0')); ts.replace("yy", PIString::fromNumber(date.year).right(2)); ts.replace("y", PIString::fromNumber(date.year).right(1)); ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0')); ts.replace("M", PIString::fromNumber(date.month)); ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0')); ts.replace("d", PIString::fromNumber(date.day)); return ts; }