/* Copyright (C) 2015 * swift Project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, * or distributed except according to the terms contained in the LICENSE file. */ //! \cond PRIVATE #include "blackmisc/aviation/aircraftsituationlist.h" #include "blackmisc/aviation/aircraftpartslist.h" #include "blackmisc/aviation/aircraftsituationchangelist.h" #include "blackmisc/aviation/liverylist.h" #include "blackmisc/aviation/aircrafticaocodelist.h" #include "blackmisc/aviation/aircraftcategorylist.h" #include "blackmisc/aviation/airlineicaocodelist.h" #include "blackmisc/aviation/airportlist.h" #include "blackmisc/db/dbinfolist.h" #include "blackmisc/db/artifactlist.h" #include "blackmisc/db/distributionlist.h" #include "blackmisc/network/textmessagelist.h" #include "blackmisc/network/rawfsdmessagelist.h" #include "blackmisc/network/urlloglist.h" #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/matchingstatistics.h" #include "blackmisc/statusmessagelist.h" #include "blackmisc/timestampobjectlist.h" #include "blackmisc/predicates.h" #include "blackmisc/identifierlist.h" #include "blackmisc/countrylist.h" #include "blackmisc/verify.h" #include "blackconfig/buildconfig.h" #include #include #include #include #include #include using namespace BlackConfig; namespace BlackMisc { template ITimestampObjectList::ITimestampObjectList() { static_assert(std::is_base_of::value, "OBJ needs to implement ITimestampBased"); } template const CONTAINER &ITimestampObjectList::container() const { return static_cast(*this); } template CONTAINER &ITimestampObjectList::container() { return static_cast(*this); } template CONTAINER ITimestampObjectList::findBefore(qint64 msSinceEpoch) const { return this->container().findBy([&](const OBJ & obj) { return obj.isOlderThan(msSinceEpoch); }); } 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) { CONTAINER result(findBefore(msSinceEpoch)); this->removeBefore(msSinceEpoch); return result; } template CONTAINER ITimestampObjectList::findBeforeNowMinusOffset(qint64 msOffset) const { return this->findBefore(QDateTime::currentMSecsSinceEpoch() - msOffset); } template CONTAINER ITimestampObjectList::findBefore(const QDateTime &dateTime) const { return this->findBefore(dateTime.toMSecsSinceEpoch()); } template CONTAINER ITimestampObjectList::findAfter(qint64 msSinceEpoc) const { return this->container().findBy([&](const OBJ & obj) { return obj.isNewerThan(msSinceEpoc); }); } template CONTAINER ITimestampObjectList::findAfterNowMinusOffset(qint64 msOffset) const { return this->findAfter(QDateTime::currentMSecsSinceEpoch() - msOffset); } 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 { return this->container().findBy([&](const OBJ & obj) { return !obj.hasValidTimestamp(); }); } template OBJ ITimestampObjectList::findClosestTimeDistance(qint64 msSinceEpoch) const { if (this->container().isEmpty()) { return OBJ(); } const auto closest = std::min_element(this->container().cbegin(), this->container().cend(), [ = ](const ITimestampBased & a, const ITimestampBased & b) { return qAbs(a.getTimeDifferenceMs(msSinceEpoch)) < qAbs(b.getTimeDifferenceMs(msSinceEpoch)); }); return *closest; } template bool ITimestampObjectList::hasInvalidTimestamps() const { return this->container().contains(&OBJ::hasValidTimestamp, false); } template void ITimestampObjectList::setCurrentUtcTime() { for (ITimestampBased &tsObj : this->container()) { tsObj.setCurrentUtcTime(); } } template void ITimestampObjectList::setUtcTime(qint64 msSinceEpoch) { for (ITimestampBased &tsObj : this->container()) { tsObj.setMSecsSinceEpoch(msSinceEpoch); } } template void ITimestampObjectList::setInvalidTimestampsToCurrentUtcTime() { for (ITimestampBased &tsObj : this->container()) { if (tsObj.hasValidTimestamp()) { continue; } tsObj.setCurrentUtcTime(); } } template QDateTime ITimestampObjectList::latestTimestamp() const { if (this->container().isEmpty()) { return QDateTime(); } return this->latestObject().getUtcTimestamp(); } template qint64 ITimestampObjectList::latestTimestampMsecsSinceEpoch() const { const QDateTime dt(latestTimestamp()); return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; } template QDateTime ITimestampObjectList::oldestTimestamp() const { if (this->container().isEmpty()) { return QDateTime(); } return this->oldestObject().getUtcTimestamp(); } template qint64 ITimestampObjectList::oldestTimestampMsecsSinceEpoch() const { const QDateTime dt(oldestTimestamp()); return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; } template OBJ ITimestampObjectList::latestObject() 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.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); return *latest; } template OBJ ITimestampObjectList::oldestObject() 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.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); }); return *oldest; } template CONTAINER ITimestampObjectList::findAfter(const QDateTime &dateTime) const { return this->findAfter(dateTime.toMSecsSinceEpoch()); } template int ITimestampObjectList::removeBefore(const QDateTime &dateTime) { return this->removeBefore(dateTime.toMSecsSinceEpoch()); } template int ITimestampObjectList::removeBefore(qint64 msSinceEpoc) { return this->container().removeIf([&](const OBJ & obj) { return obj.isOlderThan(msSinceEpoc); }); } template int ITimestampObjectList::removeOlderThanNowMinusOffset(qint64 offsetMs) { const qint64 epoch = QDateTime::currentMSecsSinceEpoch() - offsetMs; return this->container().removeIf([&](const OBJ & obj) { return obj.isOlderThan(epoch); }); } template void ITimestampObjectList::sortLatestFirst() { this->container().sortOldestFirst(); this->container().reverse(); } template void ITimestampObjectList::sortOldestFirst() { this->container().sort(Predicates::MemberLess(&OBJ::getMSecsSinceEpoch)); } template void ITimestampObjectList::push_frontKeepLatestFirst(const OBJ &value, bool replaceSameTimestamp, int maxElements) { Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); if (replaceSameTimestamp && !c.isEmpty() && c[0].getMSecsSinceEpoch() == value.getMSecsSinceEpoch()) { c[0] = value; if (maxElements > 0) { c.truncate(maxElements); } } else { if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThan(c.front()); c.push_front(value); if (needSort) { ITimestampObjectList::sortLatestFirst(); } } // crosscheck if (CBuildConfig::isLocalDeveloperDebugBuild()) { Q_ASSERT_X(this->isSortedLatestFirst(), Q_FUNC_INFO, "Wrong sort order"); } } template void ITimestampObjectList::push_backIncreaseTimestamp(const OBJ &newObject) { if (this->container().isEmpty()) { this->container().push_back(newObject); return; } const qint64 newMs = newObject.getMSecsSinceEpoch(); const qint64 oldMs = this->container().back().getMSecsSinceEpoch(); if (newMs > oldMs) { this->container().push_back(newObject); return; } this->push_backOverrideTimestamp(newObject, oldMs + 1); } template void ITimestampObjectList::push_backOverrideTimestamp(const OBJ &newObject, qint64 newTsMsSinceEpoch) { OBJ newObjectCopy(newObject); newObjectCopy.setMSecsSinceEpoch(newTsMsSinceEpoch); this->container().push_back(newObjectCopy); } template void ITimestampObjectList::setNewTimestampStartingLast(qint64 startTimeStampMs, qint64 deltaTimeMs) { if (this->container().isEmpty()) { return; } qint64 currentMs = startTimeStampMs; for (auto it = this->container().rbegin(); it != this->container().rend(); ++it) { it->setMSecsSinceEpoch(currentMs); currentMs += deltaTimeMs; } } template int ITimestampObjectList::replaceIfSameTimestamp(const OBJ &newObject) { int c = 0; for (OBJ &obj : this->container()) { if (obj.getMSecsSinceEpoch() == newObject.getMSecsSinceEpoch()) { obj = newObject; c++; } } return c; } template bool ITimestampObjectList::isSortedLatestLast() const { if (this->container().size() < 2) { return true; } qint64 max = -1; for (const ITimestampBased &obj : this->container()) { if (!obj.hasValidTimestamp()) { return false; } if (obj.getMSecsSinceEpoch() < max) { return false; } max = obj.getMSecsSinceEpoch(); } return true; } template bool ITimestampObjectList::isSortedLatestFirst() const { if (this->container().size() < 2) { return true; } qint64 min = std::numeric_limits ::max(); for (const ITimestampBased &obj : this->container()) { if (!obj.hasValidTimestamp()) { return false; } if (obj.getMSecsSinceEpoch() > min) { return false; } min = obj.getMSecsSinceEpoch(); } return true; } template void ITimestampObjectList::addMsecs(qint64 msToAdd) { if (msToAdd == 0) { return; } for (ITimestampBased &obj : this->container()) { obj.addMsecs(msToAdd); } } template void ITimestampObjectList::setSortHint(HintTimestampSort hint) { m_tsSortHint = hint; } template MillisecondsMinMaxMean ITimestampObjectList::getTimestampDifferenceMinMaxMean() const { MillisecondsMinMaxMean mmm; mmm.reset(); const CONTAINER &container = this->container(); if (container.size() < 2) { return mmm; } // Do not confuse with adjusted sort hint! if (container.m_tsSortHint == NoTimestampSortHint) { CONTAINER copy(container); copy.sortLatestFirst(); copy.m_tsSortHint = TimestampLatestFirst; return copy.getTimestampDifferenceMinMaxMean(); } mmm.max = std::numeric_limits::min(); mmm.min = std::numeric_limits::max(); qint64 mean = 0; int c = 0; OBJ last; for (const OBJ &object : container) { if (c > 0) { const ITimestampBased &l = last; const ITimestampBased &o = object; const qint64 diff = l.getAbsTimeDifferenceMs(o); if (diff > mmm.max) { mmm.max = diff; } if (diff < mmm.min) { mmm.min = diff; } mean += diff; } c++; last = object; } mmm.mean = mean / c; return mmm; } template MillisecondsMinMaxMean ITimestampWithOffsetObjectList::getOffsetMinMaxMean() const { MillisecondsMinMaxMean mmm; mmm.reset(); const CONTAINER &container = this->container(); if (container.size() < 1) { return mmm; } mmm.max = std::numeric_limits::min(); mmm.min = std::numeric_limits::max(); qint64 mean = 0; int c = 0; for (const ITimestampWithOffsetBased &object : container) { if (!object.hasNonZeroOffsetTime()) { continue; } const qint64 os = object.getTimeOffsetMs(); if (os > mmm.max) { mmm.max = os; } if (os < mmm.min) { mmm.min = os; } mean += os; c++; } if (c > 0) { mmm.mean = mean / c; } return mmm; } template void ITimestampWithOffsetObjectList::sortAdjustedLatestFirst() { this->container().sortAdjustedOldestFirst(); 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() { this->container().sort(Predicates::MemberLess(&OBJ::getAdjustedMSecsSinceEpoch)); } template bool ITimestampWithOffsetObjectList::containsZeroOrNegativeOffsetTime() const { for (const ITimestampWithOffsetBased &obj : this->container()) { if (obj.getTimeOffsetMs() <= 0) { return true; } } return false; } template bool ITimestampWithOffsetObjectList::containsNegativeOffsetTime() const { for (const ITimestampWithOffsetBased &obj : this->container()) { if (obj.getTimeOffsetMs() < 0) { return true; } } return false; } template void ITimestampWithOffsetObjectList::addMsecsToOffset(qint64 msToAdd) { for (ITimestampWithOffsetBased &obj : this->container()) { obj.addMsecsToOffsetTime(msToAdd); } } template ITimestampWithOffsetObjectList::ITimestampWithOffsetObjectList() : ITimestampObjectList() { static_assert(std::is_base_of::value, "OBJ needs to implement ITimestampBased"); } template void ITimestampWithOffsetObjectList::push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp, int maxElements) { Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); if (replaceSameTimestamp && !c.isEmpty() && c[0].getMSecsSinceEpoch() == value.getMSecsSinceEpoch()) { c[0] = value; if (maxElements > 0) { c.truncate(maxElements); } return; } if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThanAdjusted(c.front()); c.push_front(value); if (needSort) { ITimestampWithOffsetObjectList::sortAdjustedLatestFirst(); } } template void ITimestampWithOffsetObjectList::push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp, int maxElements) { ITimestampWithOffsetObjectList::push_frontKeepLatestFirst(value, replaceSameTimestamp, maxElements); // now sorted by timestamp // this reflects normally the incoming order // ts // 8: os 2 adj 12 => min os 4 // 6: os 2 adj 11 => min os 5 // 5: os 5 adj 10 // 0: os 5 adj 5 CONTAINER &c = this->container(); if (c.size() < 2) { return; } ITimestampWithOffsetBased &front = c.front(); const ITimestampWithOffsetBased &second = c[1]; if (!front.isNewerThanAdjusted(second)) { // const qint64 minOs = qMin(front.getTimeOffsetMs(), second.getTimeOffsetMs()); const qint64 minReqOs = second.getAdjustedMSecsSinceEpoch() - front.getMSecsSinceEpoch(); // minimal required const qint64 avgOs = (front.getTimeOffsetMs() + second.getTimeOffsetMs()) / 2; const qint64 os = qMax(minReqOs + 1, avgOs); // at least +1, as value must be > (greater) front.setTimeOffsetMs(os); } if (CBuildConfig::isLocalDeveloperDebugBuild()) { BLACK_VERIFY_X(front.isNewerThanAdjusted(second), Q_FUNC_INFO, "Front/second timestamp"); BLACK_VERIFY_X(this->isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order"); } } template void ITimestampWithOffsetObjectList::push_frontKeepLatestFirstIgnoreOverlapping(const OBJ &value, bool replaceSameTimestamp, int maxElements) { CONTAINER &c = this->container(); if (c.size() > 1) { const ITimestampWithOffsetBased front = c.front(); if (value.getAdjustedMSecsSinceEpoch() <= front.getAdjustedMSecsSinceEpoch()) { return; } } ITimestampWithOffsetObjectList::push_frontKeepLatestFirst(value, replaceSameTimestamp, maxElements); } template void ITimestampWithOffsetObjectList::prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs) { this->container().clear(); 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++) { OBJ copy(value); copy.addMsecs(os * i); this->container().push_back(copy); } this->setAdjustedSortHint(ITimestampWithOffsetObjectList::AdjustedTimestampLatestFirst); } 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()) { if (!obj.hasValidTimestamp()) { return false; } if (obj.getAdjustedMSecsSinceEpoch() < max) { return false; } max = obj.getAdjustedMSecsSinceEpoch(); } return true; } template bool ITimestampWithOffsetObjectList::isSortedAdjustedLatestFirst() const { if (this->container().size() < 2) { return true; } qint64 min = std::numeric_limits ::max(); for (const ITimestampWithOffsetBased &obj : this->container()) { if (!obj.hasValidTimestamp()) { return false; } if (obj.getAdjustedMSecsSinceEpoch() > min) { return false; } min = obj.getAdjustedMSecsSinceEpoch(); } 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 { if (this->container().isEmpty()) { return OBJ(); } const auto closest = std::min_element(this->container().cbegin(), this->container().cend(), [ = ](const ITimestampWithOffsetBased & a, const ITimestampWithOffsetBased & b) { return qAbs(a.getAdjustedTimeDifferenceMs(msSinceEpoch)) < qAbs(b.getAdjustedTimeDifferenceMs(msSinceEpoch)); }); return *closest; } template OBJ ITimestampWithOffsetObjectList::latestAdjustedObject() const { if (this->container().isEmpty()) { return OBJ(); } if (this->container().m_tsAdjustedSortHint == AdjustedTimestampLatestFirst) { return this->container().front(); } 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(); } if (this->container().m_tsAdjustedSortHint == AdjustedTimestampLatestFirst) { return this->container().back(); } 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 { if (this->container().isEmpty()) { return -1; } const QDateTime dt(this->latestAdjustedTimestamp()); 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 { if (this->container().isEmpty()) { return -1; } const QDateTime dt(oldestAdjustedTimestamp()); return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; } template void ITimestampWithOffsetObjectList::setAdjustedSortHint(HintAdjustedTimestampSort hint) { this->container().m_tsAdjustedSortHint = hint; } // see here for the reason of thess forward instantiations // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampWithOffsetObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampWithOffsetObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampWithOffsetObjectList; } // namespace //! \endcond