From d1f5635bd1c13074ceb2900a3982a40d915f6fc3 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 27 Apr 2018 02:11:15 +0200 Subject: [PATCH] Ref T261, timestamp utility functions --- src/blackmisc/timestampbased.cpp | 2 +- src/blackmisc/timestampbased.h | 2 +- src/blackmisc/timestampobjectlist.cpp | 162 ++++++++++++++++++++++---- src/blackmisc/timestampobjectlist.h | 54 ++++++++- 4 files changed, 192 insertions(+), 28 deletions(-) diff --git a/src/blackmisc/timestampbased.cpp b/src/blackmisc/timestampbased.cpp index 419bd5d5c..5d3270dc3 100644 --- a/src/blackmisc/timestampbased.cpp +++ b/src/blackmisc/timestampbased.cpp @@ -335,7 +335,7 @@ namespace BlackMisc switch (i) { case IndexOffsetMs: { return QVariant::fromValue(m_timeOffsetMs); } - case IndexAdjustedMisWithOffset: { return QVariant::fromValue(this->getAdjustedMSecsSinceEpoch()); } + case IndexAdjustedMsWithOffset: { return QVariant::fromValue(this->getAdjustedMSecsSinceEpoch()); } case IndexOffsetWithUnit: { return QVariant::fromValue(this->getTimeOffsetWithUnit()); } default: break; } diff --git a/src/blackmisc/timestampbased.h b/src/blackmisc/timestampbased.h index 02740849c..aee362404 100644 --- a/src/blackmisc/timestampbased.h +++ b/src/blackmisc/timestampbased.h @@ -154,7 +154,7 @@ namespace BlackMisc enum ColumnIndex { IndexOffsetMs = CPropertyIndex::GlobalIndexITimestampBased + ITimestampBased::IndexMSecsSinceEpoch + 1, - IndexAdjustedMisWithOffset, + IndexAdjustedMsWithOffset, IndexOffsetWithUnit // keep this as last item }; diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index 9e7db1548..e2c1e2005 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -28,6 +28,8 @@ #include "blackmisc/predicates.h" #include "blackmisc/identifierlist.h" #include "blackmisc/countrylist.h" +#include "blackmisc/verify.h" +#include "blackconfig/buildconfig.h" #include #include @@ -35,6 +37,8 @@ #include #include +using namespace BlackConfig; + namespace BlackMisc { template @@ -64,6 +68,14 @@ namespace BlackMisc }); } + template + OBJ ITimestampObjectList::findObjectBeforeOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER before = this->findBefore(msSinceEpoch); + if (before.isEmpty()) { return OBJ(); } + return before.latestObject(); + } + template CONTAINER ITimestampObjectList::findBeforeAndRemove(qint64 msSinceEpoch) { @@ -93,6 +105,14 @@ namespace BlackMisc }); } + template + OBJ ITimestampObjectList::findObjectAfterOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER after = this->findAfter(msSinceEpoch); + if (after.isEmpty()) { return OBJ(); } + return after.oldestObject(); + } + template CONTAINER ITimestampObjectList::findInvalidTimestamps() const { @@ -179,7 +199,7 @@ namespace BlackMisc OBJ ITimestampObjectList::latestObject() const { if (this->container().isEmpty()) { return OBJ(); } - const auto latest = std::max_element(container().begin(), container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); + const auto latest = std::max_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); return *latest; } @@ -187,7 +207,7 @@ namespace BlackMisc OBJ ITimestampObjectList::oldestObject() const { if (this->container().isEmpty()) { return OBJ(); } - const auto oldest = std::min_element(container().begin(), container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); + const auto oldest = std::min_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); return *oldest; } @@ -238,11 +258,9 @@ namespace BlackMisc template void ITimestampObjectList::push_frontKeepLatestFirst(const OBJ &value, int maxElements) { + Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); - if (maxElements > 0 && c.size() >= maxElements) - { - c.truncate(maxElements - 1); - } + if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThan(c.front()); c.push_front(value); if (needSort) @@ -296,6 +314,23 @@ namespace BlackMisc this->container().reverse(); } + template + CONTAINER ITimestampWithOffsetObjectList::getSortedAdjustedLatestFirst() const + { + CONTAINER copy(this->container()); + copy.sortAdjustedLatestFirst(); + return copy; + } + + template + CONTAINER ITimestampWithOffsetObjectList::getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst) const + { + if (this->container().size() < 2) { return CONTAINER(); } + CONTAINER copy(alreadySortedLatestFirst ? this->container() : this->container().getSortedAdjustedLatestFirst()); + copy.truncate(2); + return copy; + } + template void ITimestampWithOffsetObjectList::sortAdjustedOldestFirst() { @@ -342,10 +377,7 @@ namespace BlackMisc { Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); - if (maxElements > 0 && c.size() >= maxElements) - { - c.truncate(maxElements - 1); - } + if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThanAdjusted(c.front()); c.push_front(value); if (needSort) @@ -361,27 +393,37 @@ namespace BlackMisc // now sorted by timestamp // this reflects normally the incoming order - CONTAINER &c = this->container(); - if (c.size() < 2) { return; } - const ITimestampWithOffsetBased &front = c.front(); - ITimestampWithOffsetBased &second = c[1]; - if (!front.isOlderThanAdjusted(second)) { return; } - const qint64 maxOs = front.getAdjustedMSecsSinceEpoch() - second.getMSecsSinceEpoch(); - const qint64 avgOs = (maxOs + qMax(front.getTimeOffsetMs(), maxOs)) / 2; - second.setTimeOffsetMs(avgOs); - // ts // 8: os 2 adj 10 // 6: os 2 adj 8 // 5: os 5 adj 10 => max os 3 // 0: os 5 adj 5 + CONTAINER &c = this->container(); + if (c.size() < 2) { return; } + const ITimestampWithOffsetBased &front = c.front(); + ITimestampWithOffsetBased &second = c[1]; + if (front.isNewerThanAdjusted(second)) { return; } + const qint64 diffOs = front.getAdjustedMSecsSinceEpoch() - second.getMSecsSinceEpoch() - 1; + const qint64 avgOs = (diffOs + qMin(front.getTimeOffsetMs(), diffOs)) / 2; + second.setTimeOffsetMs(avgOs); + + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(front.isNewerThanAdjusted(second), Q_FUNC_INFO, "Front/second timestamp"); + Q_ASSERT_X(this->isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order"); + } } template void ITimestampWithOffsetObjectList::prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs) { this->container().clear(); - const qint64 os = -1 * qAbs(deltaTimeMs < 0 ? value.getTimeOffsetMs() : deltaTimeMs); + const qint64 osTime = value.getTimeOffsetMs(); + const qint64 os = -1 * qAbs(deltaTimeMs < 0 ? osTime : deltaTimeMs); + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + BLACK_VERIFY_X(os < 0, Q_FUNC_INFO, "Need negative offset time to prefill time"); + } this->container().push_front(value); for (int i = 1; i < elements; i++) { @@ -394,6 +436,7 @@ namespace BlackMisc template bool ITimestampWithOffsetObjectList::isSortedAdjustedLatestLast() const { + if (this->container().isEmpty()) { return false; } if (this->container().size() < 2) { return true; } qint64 max = -1; for (const ITimestampWithOffsetBased &obj : this->container()) @@ -408,6 +451,7 @@ namespace BlackMisc template bool ITimestampWithOffsetObjectList::isSortedAdjustedLatestFirst() const { + if (this->container().isEmpty()) { return false; } if (this->container().size() < 2) { return true; } qint64 min = std::numeric_limits ::max(); for (const ITimestampWithOffsetBased &obj : this->container()) @@ -419,6 +463,40 @@ namespace BlackMisc return true; } + template + CONTAINER ITimestampWithOffsetObjectList::findAfterAdjusted(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const ITimestampWithOffsetBased & obj) + { + return obj.isNewerThanAdjusted(msSinceEpoch); + }); + } + + template + OBJ ITimestampWithOffsetObjectList::findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER after = this->findAfterAdjusted(msSinceEpoch); + if (after.isEmpty()) { return OBJ(); } + return after.oldestAdjustedObject(); + } + + template + CONTAINER ITimestampWithOffsetObjectList::findBeforeAdjusted(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const ITimestampWithOffsetBased & obj) + { + return obj.isOlderThanAdjusted(msSinceEpoch); + }); + } + + template + OBJ ITimestampWithOffsetObjectList::findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER before = this->findBeforeAdjusted(msSinceEpoch); + if (before.isEmpty()) { return OBJ(); } + return before.latestAdjustedObject(); + } + template OBJ ITimestampWithOffsetObjectList::findClosestTimeDistanceAdjusted(qint64 msSinceEpoch) const { @@ -430,6 +508,50 @@ namespace BlackMisc return *closest; } + template + OBJ ITimestampWithOffsetObjectList::latestAdjustedObject() const + { + if (this->container().isEmpty()) { return OBJ(); } + const auto latest = std::max_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); }); + return *latest; + } + + template + OBJ ITimestampWithOffsetObjectList::oldestAdjustedObject() const + { + if (this->container().isEmpty()) { return OBJ(); } + const auto oldest = std::min_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); }); + return *oldest; + } + + template + QDateTime ITimestampWithOffsetObjectList::latestAdjustedTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->latestAdjustedObject().getUtcTimestamp(); + } + + template + qint64 ITimestampWithOffsetObjectList::latestAdjustedTimestampMsecsSinceEpoch() const + { + const QDateTime dt(this->latestTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } + + template + QDateTime ITimestampWithOffsetObjectList::oldestAdjustedTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->oldestAdjustedObject().getUtcTimestamp(); + } + + template + qint64 ITimestampWithOffsetObjectList::oldestAdjustedTimestampMsecsSinceEpoch() const + { + const QDateTime dt(oldestAdjustedTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } + // see here for the reason of thess forward instantiations // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl //! \cond PRIVATE diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 67171c916..eb99212e7 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -26,24 +26,30 @@ namespace BlackMisc template class ITimestampObjectList { public: - //! List of objects before dateTime + //! List of objects before dateTime (older) CONTAINER findBefore(const QDateTime &dateTime) const; - //! List of objects before msSinceEpoch + //! List of objects before msSinceEpoch (older) CONTAINER findBefore(qint64 msSinceEpoch) const; + //! Object before timestamp or default (older) + OBJ findObjectBeforeOrDefault(qint64 msSinceEpoch) const; + //! Get objects before msSinceEpoch and remove those CONTAINER findBeforeAndRemove(qint64 msSinceEpoch); //! List of objects before now - offset CONTAINER findBeforeNowMinusOffset(qint64 msOffset) const; - //! List of objects after dateTime + //! List of objects after dateTime (newer) CONTAINER findAfter(const QDateTime &dateTime) const; - //! List of objects after msSinceEpoch + //! List of objects after msSinceEpoch (newer) CONTAINER findAfter(qint64 msSinceEpoch) const; + //! List of objects after msSinceEpoch (newer) + OBJ findObjectAfterOrDefault(qint64 msSinceEpoch) const; + //! Objects without valid timestamp CONTAINER findInvalidTimestamps() const; @@ -74,10 +80,10 @@ namespace BlackMisc //! Oldest timestamp qint64 oldestTimestampMsecsSinceEpoch() const; - //! Latest value + //! Latest object OBJ latestObject() const; - //! Latest value + //! Latest object OBJ oldestObject() const; //! Remove objects with timestamp before dateTime @@ -128,6 +134,12 @@ namespace BlackMisc //! Sort by adjusted timestamp void sortAdjustedLatestFirst(); + //! As sorted copy + CONTAINER getSortedAdjustedLatestFirst() const; + + //! Get the latest 2 values + CONTAINER getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst = false) const; + //! Sort by adjusted timestamp void sortAdjustedOldestFirst(); @@ -158,9 +170,39 @@ namespace BlackMisc //! \remark all object must have a valid timestamp bool isSortedAdjustedLatestFirst() const; + //! List of objects after msSinceEpoch (newer) + CONTAINER findAfterAdjusted(qint64 msSinceEpoch) const; + + //! List of objects after msSinceEpoch (newer) + OBJ findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const; + + //! List of objects before msSinceEpoch (older) + CONTAINER findBeforeAdjusted(qint64 msSinceEpoch) const; + + //! Object before timestamp (older) + OBJ findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const; + //! Closest adjusted time difference OBJ findClosestTimeDistanceAdjusted(qint64 msSinceEpoch) const; + //! Latest adjusted object + OBJ latestAdjustedObject() const; + + //! Oldest adjusted object + OBJ oldestAdjustedObject() const; + + //! Latest adjusted timestamp + QDateTime latestAdjustedTimestamp() const; + + //! Oldest adjusted timestamp + QDateTime oldestAdjustedTimestamp() const; + + //! Latest adjusted timestamp + qint64 latestAdjustedTimestampMsecsSinceEpoch() const; + + //! Oldest adjusted timestamp + qint64 oldestAdjustedTimestampMsecsSinceEpoch() const; + protected: //! Constructor ITimestampWithOffsetObjectList();