diff --git a/include/protocols/MeterS0.hpp b/include/protocols/MeterS0.hpp index 6ba3e098..60acdcf1 100644 --- a/include/protocols/MeterS0.hpp +++ b/include/protocols/MeterS0.hpp @@ -32,6 +32,12 @@ #include +// some helper functions. might need a namespace +void timespec_sub(const struct timespec &a, const struct timespec &b, struct timespec &res); +void timespec_add_ms(struct timespec &a, unsigned long ms); +unsigned long timespec_sub_ms(const struct timespec &a, const struct timespec &b); + + class MeterS0 : public vz::protocol::Protocol { public: class HWIF { @@ -104,6 +110,7 @@ class MeterS0 : public vz::protocol::Protocol { protected: void counter_thread(); + void check_ref_for_overflow(); HWIF * _hwif; HWIF * _hwif_dir; // for dir gpio pin @@ -119,7 +126,8 @@ class MeterS0 : public vz::protocol::Protocol { int _nonblocking_delay_ns; struct timespec _time_last_read; // timestamp of last read. 1s interval based on this timestamp - std::atomic _time_last_impulse; // timestamp of last impulse + struct timespec _time_last_ref; // reference timestamp for the millisecond delta + std::atomic _ms_last_impulse; // ms of last impulse relative to _time_last_ref struct timespec _time_last_impulse_returned; // timestamp of last impulse returned bool _first_impulse; }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9a1691b..b3c7bfd5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,7 +70,7 @@ if( TARGET ) target_link_libraries(vzlogger dl) endif( ${TARGET} STREQUAL "ar71xx") endif( TARGET ) -target_link_libraries(vzlogger ${CURL_STATIC_LIBRARIES} ${CURL_LIBRARIES} ${GNUTLS_LIBRARIES} ${OPENSSL_LIBRARIES} atomic) +target_link_libraries(vzlogger ${CURL_STATIC_LIBRARIES} ${CURL_LIBRARIES} ${GNUTLS_LIBRARIES} ${OPENSSL_LIBRARIES} ) # add programs to the install target INSTALL(PROGRAMS diff --git a/src/protocols/CMakeLists.txt b/src/protocols/CMakeLists.txt index f5020352..216f4cce 100644 --- a/src/protocols/CMakeLists.txt +++ b/src/protocols/CMakeLists.txt @@ -25,8 +25,8 @@ else ( OMS_SUPPORT ) endif( OMS_SUPPORT ) set(proto_srcs - MeterS0.cpp - MeterD0.cpp + MeterS0.cpp ../../include/protocols/MeterS0.hpp + MeterD0.cpp ../../include/protocols/MeterD0.hpp ${sml_srcs} MeterFluksoV2.cpp ${ocr_srcs} diff --git a/src/protocols/MeterS0.cpp b/src/protocols/MeterS0.cpp index a4d4277b..723332cb 100644 --- a/src/protocols/MeterS0.cpp +++ b/src/protocols/MeterS0.cpp @@ -156,7 +156,46 @@ void timespec_sub(const struct timespec &a, const struct timespec &b, struct tim res.tv_nsec = a.tv_nsec - b.tv_nsec; if (res.tv_nsec < 0) { --res.tv_sec; - res.tv_nsec += 1e9; + res.tv_nsec += 1000000000ul; + } +} + +unsigned long timespec_sub_ms(const struct timespec &a, const struct timespec &b) +{ + unsigned long ret; + ret = a.tv_sec - b.tv_sec; + if (a.tv_nsec < b.tv_nsec) { + --ret; + ret *= 1000ul; + ret += (1000000000ul + a.tv_nsec - b.tv_nsec) / 1000000ul; + } else { + ret *= 1000ul; + ret += (a.tv_nsec - b.tv_nsec) / 1000000ul; + } + return ret; +} + +void timespec_add_ms(struct timespec &a, unsigned long ms) +{ + a.tv_sec += ms/1000ul; + a.tv_nsec += (ms%1000ul)*1000000ul; + // normalize nsec + while (a.tv_nsec >= 1000000000l) { + ++a.tv_sec; + a.tv_nsec -= 1000000000l; + } +} + +void MeterS0::check_ref_for_overflow() +{ + // check whether _ms_last_impulse get's too long + // and has risk for overflow (roughly once a month with 32bit unsigned long) + + if (_ms_last_impulse > (1ul<<30)) { + // now we enter a race condition so there might be wrong impulse now! + timespec_add_ms(_time_last_ref, 1ul<<30 ); + _ms_last_impulse -= 1ul << 30; + } } @@ -186,7 +225,7 @@ void MeterS0::counter_thread() if (_hwif->waitForImpulse()) { struct timespec temp_ts; clock_gettime(CLOCK_REALTIME, &temp_ts); - _time_last_impulse = temp_ts; // uses atomic operator= + _ms_last_impulse = timespec_sub_ms(temp_ts, _time_last_ref); // uses atomic operator= if (_hwif_dir && ( _hwif_dir->status()>0 ) ) ++_impulses_neg; else @@ -243,15 +282,16 @@ int MeterS0::open() { _impulses = 0; _impulses_neg = 0; - // create counter_thread and pass this as param - _counter_thread_stop = false; - _counter_thread = std::thread(&MeterS0::counter_thread, this); - clock_gettime(CLOCK_REALTIME, &_time_last_read); // we use realtime as this is returned as well (clock_monotonic would be better but...) // store current time as last_time. Next read will return after 1s. - _time_last_impulse = _time_last_read; + _time_last_ref = _time_last_read; + _ms_last_impulse = 0; _time_last_impulse_returned = _time_last_read; + // create counter_thread and pass this as param + _counter_thread_stop = false; + _counter_thread = std::thread(&MeterS0::counter_thread, this); + print(log_finest, "counter_thread created", name().c_str()); return SUCCESS; @@ -306,7 +346,9 @@ ssize_t MeterS0::read(std::vector &rds, size_t n) { if (_hwif->is_blocking()) { // we use the time from last impulse t1 = _time_last_impulse_returned.tv_sec + _time_last_impulse_returned.tv_nsec / 1e9; - struct timespec temp_ts = _time_last_impulse; // uses atomic operator T + struct timespec temp_ts = _time_last_ref; + timespec_add_ms(temp_ts, _ms_last_impulse); + check_ref_for_overflow(); t2 = temp_ts.tv_sec + temp_ts.tv_nsec / 1e9; _time_last_impulse_returned = temp_ts; _time_last_read = req; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1b46c5a3..db8fde02 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -38,7 +38,7 @@ target_link_libraries(vzlogger_unit_tests ${LIBUUID} dl pthread) -target_link_libraries(vzlogger_unit_tests ${CURL_STATIC_LIBRARIES} ${CURL_LIBRARIES} ${GNUTLS_LIBRARIES} ${OCR_LIBRARIES} atomic) +target_link_libraries(vzlogger_unit_tests ${CURL_STATIC_LIBRARIES} ${CURL_LIBRARIES} ${GNUTLS_LIBRARIES} ${OCR_LIBRARIES}) if( OMS_SUPPORT ) target_link_libraries(vzlogger_unit_tests ${MBUS_LIBRARY} ${OPENSSL_LIBRARIES}) diff --git a/tests/mocks/CMakeLists.txt b/tests/mocks/CMakeLists.txt index d83090aa..73b62114 100644 --- a/tests/mocks/CMakeLists.txt +++ b/tests/mocks/CMakeLists.txt @@ -49,7 +49,7 @@ target_link_libraries(mock_metermap pthread ${JSON_LIBRARY} ${LIBUUID} - dl atomic + dl ) if(SML_FOUND) target_link_libraries(mock_metermap ${SML_LIBRARY}) @@ -114,7 +114,7 @@ target_link_libraries(mock_MeterS0 pthread ${JSON_LIBRARY} ${LIBUUID} - dl atomic + dl ) add_test(mock_MeterS0 mock_MeterS0) diff --git a/tests/mocks/mock_MeterS0.cpp b/tests/mocks/mock_MeterS0.cpp index a523b6b4..37db8299 100644 --- a/tests/mocks/mock_MeterS0.cpp +++ b/tests/mocks/mock_MeterS0.cpp @@ -31,6 +31,59 @@ class mock_S0hwif : public MeterS0::HWIF protected: }; +TEST(mock_MeterS0, timespec_add_ms) +{ + struct timespec a; + a.tv_sec = 1; + a.tv_nsec = 0; + timespec_add_ms(a, 1001); + ASSERT_EQ(2, a.tv_sec); + ASSERT_EQ(1000000, a.tv_nsec); +} + +TEST(mock_MeterS0, timespec_add_ms2) +{ + struct timespec a; + a.tv_sec = 1; + a.tv_nsec = 1000000000; + timespec_add_ms(a, 1000); + EXPECT_EQ(3, a.tv_sec); + ASSERT_EQ(0, a.tv_nsec); +} + +TEST(mock_MeterS0, timespec_add_ms3) +{ + struct timespec a; + a.tv_sec = 1; + a.tv_nsec = 1001000000; + timespec_add_ms(a, 1999); + EXPECT_EQ(4, a.tv_sec); + EXPECT_EQ(0, a.tv_nsec); +} + +TEST(mock_MeterS0, timespec_sub_ms) +{ + struct timespec a; + a.tv_sec = 1; + a.tv_nsec = 0; + struct timespec b; + b=a; + timespec_add_ms(a, 1999); + EXPECT_EQ(1999ul, timespec_sub_ms(a, b) ); +} + +TEST(mock_MeterS0, timespec_sub_ms2) +{ + struct timespec a; + a.tv_sec = 2; + a.tv_nsec = 0; + struct timespec b; + b.tv_sec = 1; + b.tv_nsec = 1000000; + EXPECT_EQ(999ul, timespec_sub_ms(a, b) ); +} + + TEST(mock_MeterS0, basic_noopen) { mock_S0hwif *hwif = new mock_S0hwif();