From 3c3b4cd2fe24ac93d6e4027c171c4258e61e73a6 Mon Sep 17 00:00:00 2001 From: Mat Sutcliffe Date: Tue, 3 Nov 2020 22:56:05 +0000 Subject: [PATCH] Issue #77 Move some templated member functions into their respective header files --- samples/blackmisc/samplesperformance.h | 1 + src/blackcore/context/contextnetwork.h | 6 +- src/blackcore/fsd/fsdclient.h | 2 + .../components/dbquickmappingwizard.h | 2 +- .../components/textmessagecomponent.h | 1 + src/blackgui/editors/aircraftmodelform.h | 3 + src/blackmisc/aviation/callsignobjectlist.cpp | 311 ------- src/blackmisc/aviation/callsignobjectlist.h | 287 +++++-- src/blackmisc/db/datastoreobjectlist.cpp | 381 --------- src/blackmisc/db/datastoreobjectlist.h | 323 +++++++- src/blackmisc/geo/geoobjectlist.cpp | 314 ------- src/blackmisc/geo/geoobjectlist.h | 288 +++++-- src/blackmisc/network/urllog.cpp | 2 - src/blackmisc/orderablelist.cpp | 191 ----- src/blackmisc/orderablelist.h | 167 +++- src/blackmisc/simulation/distributorlist.h | 1 + .../fscommon/aircraftcfgentries.cpp | 1 - .../fscommon/aircraftcfgentrieslist.cpp | 1 - .../simulation/fscommon/vpilotmodelrule.cpp | 1 - .../fscommon/vpilotmodelruleset.cpp | 2 - .../simulation/fscommon/vpilotrulesreader.cpp | 1 - src/blackmisc/timestampobjectlist.cpp | 775 ------------------ src/blackmisc/timestampobjectlist.h | 720 ++++++++++++---- .../emulated/simulatoremulatedmonitordialog.h | 1 + 24 files changed, 1438 insertions(+), 2344 deletions(-) delete mode 100644 src/blackmisc/aviation/callsignobjectlist.cpp delete mode 100644 src/blackmisc/db/datastoreobjectlist.cpp delete mode 100644 src/blackmisc/geo/geoobjectlist.cpp delete mode 100644 src/blackmisc/orderablelist.cpp delete mode 100644 src/blackmisc/timestampobjectlist.cpp diff --git a/samples/blackmisc/samplesperformance.h b/samples/blackmisc/samplesperformance.h index 533bbc7b5..956ee4e09 100644 --- a/samples/blackmisc/samplesperformance.h +++ b/samples/blackmisc/samplesperformance.h @@ -14,6 +14,7 @@ #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/aviation/aircraftsituationlist.h" +#include "blackmisc/aviation/atcstationlist.h" #include "blackmisc/aviation/callsignset.h" #include #include diff --git a/src/blackcore/context/contextnetwork.h b/src/blackcore/context/contextnetwork.h index 75baea370..081f5a209 100644 --- a/src/blackcore/context/contextnetwork.h +++ b/src/blackcore/context/contextnetwork.h @@ -64,7 +64,11 @@ namespace BlackMisc class CAircraftParts; class CCallsign; } - namespace Network { class CTextMessage; } + namespace Network + { + class CRawFsdMessage; + class CTextMessage; + } namespace Simulation { class CAircraftModel; } } namespace BlackCore diff --git a/src/blackcore/fsd/fsdclient.h b/src/blackcore/fsd/fsdclient.h index f5dd1f2bb..c3e8f926e 100644 --- a/src/blackcore/fsd/fsdclient.h +++ b/src/blackcore/fsd/fsdclient.h @@ -20,8 +20,10 @@ #include "blackmisc/simulation/remoteaircraftprovider.h" #include "blackmisc/simulation/simulationenvironmentprovider.h" #include "blackmisc/aviation/callsign.h" +#include "blackmisc/aviation/flightplan.h" #include "blackmisc/aviation/informationmessage.h" #include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/network/rawfsdmessage.h" #include "blackmisc/network/connectionstatus.h" #include "blackmisc/network/loginmode.h" #include "blackmisc/network/server.h" diff --git a/src/blackgui/components/dbquickmappingwizard.h b/src/blackgui/components/dbquickmappingwizard.h index b00fef9e9..03b1c7673 100644 --- a/src/blackgui/components/dbquickmappingwizard.h +++ b/src/blackgui/components/dbquickmappingwizard.h @@ -11,7 +11,7 @@ #ifndef BLACKGUI_COMPONENTS_DBQUICKMAPPINGWIZARD_H #define BLACKGUI_COMPONENTS_DBQUICKMAPPINGWIZARD_H -#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/aviation/aircrafticaocode.h" #include #include diff --git a/src/blackgui/components/textmessagecomponent.h b/src/blackgui/components/textmessagecomponent.h index 8359c914e..cf472a27e 100644 --- a/src/blackgui/components/textmessagecomponent.h +++ b/src/blackgui/components/textmessagecomponent.h @@ -16,6 +16,7 @@ #include "blackgui/enablefordockwidgetinfoarea.h" #include "blackgui/blackguiexport.h" #include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/network/textmessagelist.h" #include "blackmisc/aviation/atcstation.h" #include "blackmisc/audio/audiosettings.h" #include "blackmisc/identifier.h" diff --git a/src/blackgui/editors/aircraftmodelform.h b/src/blackgui/editors/aircraftmodelform.h index a091e627c..5a7f335cc 100644 --- a/src/blackgui/editors/aircraftmodelform.h +++ b/src/blackgui/editors/aircraftmodelform.h @@ -12,6 +12,9 @@ #define BLACKGUI_EDITORS_AIRCRAFTMODELFORM_H #include "form.h" +#include "blackmisc/aviation/livery.h" +#include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/simulation/distributor.h" namespace Ui { class CAircraftModelForm; } namespace BlackGui diff --git a/src/blackmisc/aviation/callsignobjectlist.cpp b/src/blackmisc/aviation/callsignobjectlist.cpp deleted file mode 100644 index 348190e32..000000000 --- a/src/blackmisc/aviation/callsignobjectlist.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* 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. - */ - -#include "blackmisc/simulation/simulatedaircraftlist.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/interpolationsetuplist.h" -#include "blackmisc/aviation/callsignobjectlist.h" -#include "blackmisc/aviation/atcstationlist.h" -#include "blackmisc/aviation/aircraftsituationlist.h" -#include "blackmisc/aviation/flightplanlist.h" -#include "blackmisc/network/clientlist.h" -#include "blackmisc/network/userlist.h" -#include "blackmisc/predicates.h" - -#include - -namespace BlackMisc -{ - namespace Aviation - { - template - ICallsignObjectList::ICallsignObjectList() - { } - - template - const CONTAINER &ICallsignObjectList::container() const - { - return static_cast(*this); - } - - template - CONTAINER &ICallsignObjectList::container() - { - return static_cast(*this); - } - - template - bool ICallsignObjectList::containsCallsign(const CCallsign &callsign) const - { - return this->container().contains(&OBJ::getCallsign, callsign); - } - - template - int ICallsignObjectList::applyIfCallsign(const CCallsign &callsign, const CPropertyIndexVariantMap &variantMap, bool skipEqualValues) - { - return this->container().applyIf(&OBJ::getCallsign, callsign, variantMap, skipEqualValues); - } - - template - CCallsignSet ICallsignObjectList::getCallsigns() const - { - CCallsignSet cs; - for (const OBJ &obj : this->container()) - { - cs.push_back(obj.getCallsign()); - } - return cs; - } - - template - QStringList ICallsignObjectList::getCallsignStrings(bool sorted) const - { - return this->getCallsigns().getCallsignStrings(sorted); - } - - template - QString ICallsignObjectList::getCallsignsAsString(const QString &separator, bool sorted) const - { - if (this->container().isEmpty()) { return QString(); } - const QStringList callsigns = this->getCallsignStrings(sorted); - return callsigns.join(separator); - } - - template - CONTAINER ICallsignObjectList::findByCallsign(const CCallsign &callsign) const - { - return this->container().findBy(&OBJ::getCallsign, callsign); - } - - template - CONTAINER ICallsignObjectList::findByCallsigns(const CCallsignSet &callsigns) const - { - return this->container().findBy(Predicates::MemberIsAnyOf(&OBJ::getCallsign, callsigns)); - } - - template - OBJ ICallsignObjectList::findFirstByCallsign(const CCallsign &callsign, const OBJ &ifNotFound) const - { - return this->container().findFirstByOrDefault(&OBJ::getCallsign, callsign, ifNotFound); - } - - template - OBJ ICallsignObjectList::findLastByCallsign(const CCallsign &callsign, const OBJ &ifNotFound) const - { - for (auto current = container().end(); current != container().begin() ; /* Do nothing */) - { - --current; - if (current->getCallsign() == callsign) { return *current; } - } - return ifNotFound; - } - - template - CONTAINER ICallsignObjectList::findBySuffix(const QString &suffix) const - { - CONTAINER r; - if (suffix.isEmpty()) { return r; } - const QString sfxUpper(suffix.trimmed().toUpper()); - r = this->container().findBy([ = ](const OBJ & csObj) - { - return (csObj.getCallsign().getSuffix() == sfxUpper); - }); - return r; - } - - template - int ICallsignObjectList::firstIndexOfCallsign(const CCallsign &callsign) - { - for (int i = 0; i < this->container().size(); i++) - { - if (this->container()[i].getCallsign() == callsign) { return i; } - } - return -1; - } - - template - int ICallsignObjectList::removeByCallsign(const CCallsign &callsign) - { - return this->container().removeIf(&OBJ::getCallsign, callsign); - } - - template - int ICallsignObjectList::removeByCallsigns(const CCallsignSet &callsigns) - { - return this->container().removeIf([ & ](const OBJ & obj) { return callsigns.contains(obj.getCallsign()); }); - } - - template - QMap ICallsignObjectList::getSuffixesAndCount() const - { - QMap r; // sorted by key - for (const OBJ &csObj : this->container()) - { - const QString s = csObj.getCallsign().getSuffix(); - if (s.isEmpty()) { continue; } - if (r.contains(s)) - { - r[s] = r[s] + 1; - } - else - { - r.insert(s, 1); - } - } - return r; - } - - template - QStringList ICallsignObjectList::getSuffixes() const - { - QStringList suffixes; - for (const OBJ &csObj : this->container()) - { - const QString s = csObj.getCallsign().getSuffix(); - if (s.isEmpty() || suffixes.contains(s, Qt::CaseInsensitive)) { continue; } - suffixes << s; - } - return suffixes; - } - - template - QHash ICallsignObjectList::splitPerCallsign() const - { - CONTAINER copyContainer(container()); - copyContainer.sortByCallsign(); - QHash result; - CCallsign cs; - for (const OBJ &csObj : copyContainer) - { - if (csObj.getCallsign().isEmpty()) - { - Q_ASSERT(false); // there should be no empty callsigns - continue; - } - if (cs != csObj.getCallsign()) - { - cs = csObj.getCallsign(); - CONTAINER perCallsign({ csObj }); - result.insert(cs, perCallsign); - } - else - { - result[cs].push_back(csObj); - } - } - return result; - } - - template - int ICallsignObjectList::replaceOrAddObjectByCallsign(const OBJ &otherObject) - { - const CCallsign cs(otherObject.getCallsign()); - if (cs.isEmpty()) { return 0; } - CONTAINER ©(this->container()); - copy.removeByCallsign(cs); - copy.push_back(otherObject); - return 1; - } - - template - int ICallsignObjectList::replaceOrAddObjectsByCallsign(const CONTAINER &others) - { - if (others.isEmpty()) { return 0; } - int c = 0; - CONTAINER copy(this->container()); - for (const OBJ &obj : others) - { - const CCallsign cs(obj.getCallsign()); - if (cs.isEmpty()) { continue; } - copy.removeByCallsign(cs); - copy.push_back(obj); - c++; - } - *this = copy; - return c; - } - - template - void ICallsignObjectList::sortByCallsign() - { - container().sortBy(&OBJ::getCallsign); - } - - template - QMap ICallsignObjectList::asCallsignMap() const - { - QMap map; - for (const OBJ &obj : this->container()) - { - map.insert(obj.getCallsign(), obj); - } - return map; - } - - template - QHash ICallsignObjectList::asCallsignHash() const - { - QHash hash; - for (const OBJ &obj : this->container()) - { - if (obj.getCallsign().isEmpty()) { continue; } - hash.insert(obj.getCallsign(), obj); - } - return hash; - } - - template - CONTAINER ICallsignObjectList::sortedByCallsign() const - { - CONTAINER copy(this->container()); - copy.sortByCallsign(); - return copy; - } - - template - int ICallsignObjectList::incrementalUpdateOrAdd(const OBJ &objectBeforeChanges, const CPropertyIndexVariantMap &changedValues) - { - int c; - const CCallsign cs = objectBeforeChanges.getCallsign(); - if (this->containsCallsign(cs)) - { - if (changedValues.isEmpty()) { return 0; } - c = this->container().applyIf(&OBJ::getCallsign, cs, changedValues); - } - else - { - c = 1; - if (changedValues.isEmpty()) - { - this->container().push_back(objectBeforeChanges); - } - else - { - OBJ objectAdded(objectBeforeChanges); - objectAdded.apply(changedValues); - this->container().push_back(objectAdded); - } - } - return c; - } - - // see here for the reason of thess forward instantiations - // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl - //! \cond PRIVATE - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ICallsignObjectList; - //! \endcond - - } // namespace -} // namespace diff --git a/src/blackmisc/aviation/callsignobjectlist.h b/src/blackmisc/aviation/callsignobjectlist.h index 276178911..b3f235970 100644 --- a/src/blackmisc/aviation/callsignobjectlist.h +++ b/src/blackmisc/aviation/callsignobjectlist.h @@ -13,40 +13,14 @@ #include "blackmisc/aviation/callsignset.h" #include "blackmisc/propertyindexvariantmap.h" +#include "blackmisc/predicates.h" +#include #include #include namespace BlackMisc { - namespace Aviation - { - class CAircraftSituation; - class CAircraftSituationList; - class CAtcStation; - class CAtcStationList; - class CFlightPlan; - class CFlightPlanList; - } - - namespace Simulation - { - class CSimulatedAircraft; - class CSimulatedAircraftList; - class CAircraftModel; - class CAircraftModelList; - class CInterpolationAndRenderingSetupPerCallsign; - class CInterpolationSetupList; - } - - namespace Network - { - class CClient; - class CClientList; - class CUser; - class CUserList; - } - namespace Aviation { //! List of objects with callsign. @@ -55,98 +29,279 @@ namespace BlackMisc { public: //! Contains callsign? - bool containsCallsign(const CCallsign &callsign) const; + bool containsCallsign(const CCallsign &callsign) const + { + return this->container().contains(&OBJ::getCallsign, callsign); + } //! Apply for given callsign - int applyIfCallsign(const CCallsign &callsign, const CPropertyIndexVariantMap &variantMap, bool skipEqualValues = true); + int applyIfCallsign(const CCallsign &callsign, const CPropertyIndexVariantMap &variantMap, bool skipEqualValues = true) + { + return this->container().applyIf(&OBJ::getCallsign, callsign, variantMap, skipEqualValues); + } //! All callsigns - BlackMisc::Aviation::CCallsignSet getCallsigns() const; + BlackMisc::Aviation::CCallsignSet getCallsigns() const + { + CCallsignSet cs; + for (const OBJ &obj : this->container()) + { + cs.push_back(obj.getCallsign()); + } + return cs; + } //! Get callsign string list - QStringList getCallsignStrings(bool sorted = false) const; + QStringList getCallsignStrings(bool sorted = false) const + { + return this->getCallsigns().getCallsignStrings(sorted); + } //! Get callsigns as strings - QString getCallsignsAsString(const QString &separator, bool sorted = false) const; + QString getCallsignsAsString(const QString &separator, bool sorted = false) const + { + if (this->container().isEmpty()) { return QString(); } + const QStringList callsigns = this->getCallsignStrings(sorted); + return callsigns.join(separator); + } //! Find 0..n stations by callsign - CONTAINER findByCallsign(const CCallsign &callsign) const; + CONTAINER findByCallsign(const CCallsign &callsign) const + { + return this->container().findBy(&OBJ::getCallsign, callsign); + } //! Find 0..n aircraft matching any of a set of callsigns - CONTAINER findByCallsigns(const CCallsignSet &callsigns) const; + CONTAINER findByCallsigns(const CCallsignSet &callsigns) const + { + return this->container().findBy(Predicates::MemberIsAnyOf(&OBJ::getCallsign, callsigns)); + } //! Find the first aircraft by callsign, if none return given one - OBJ findFirstByCallsign(const CCallsign &callsign, const OBJ &ifNotFound = {}) const; + OBJ findFirstByCallsign(const CCallsign &callsign, const OBJ &ifNotFound = {}) const + { + return this->container().findFirstByOrDefault(&OBJ::getCallsign, callsign, ifNotFound); + } //! Find the back object by callsign, if none return given one - OBJ findLastByCallsign(const CCallsign &callsign, const OBJ &ifNotFound = {}) const; + OBJ findLastByCallsign(const CCallsign &callsign, const OBJ &ifNotFound = {}) const + { + for (auto current = container().end(); current != container().begin(); /* Do nothing */) + { + --current; + if (current->getCallsign() == callsign) { return *current; } + } + return ifNotFound; + } //! All with given suffix, empty suffixes ignored - CONTAINER findBySuffix(const QString &suffix) const; + CONTAINER findBySuffix(const QString &suffix) const + { + CONTAINER r; + if (suffix.isEmpty()) { return r; } + const QString sfxUpper(suffix.trimmed().toUpper()); + r = this->container().findBy([=](const OBJ & csObj) + { + return (csObj.getCallsign().getSuffix() == sfxUpper); + }); + return r; + } //! First found index of callsign, otherwise -1 - int firstIndexOfCallsign(const CCallsign &callsign); + int firstIndexOfCallsign(const CCallsign &callsign) + { + for (int i = 0; i < this->container().size(); i++) + { + if (this->container()[i].getCallsign() == callsign) { return i; } + } + return -1; + } //! Remove all objects with callsign - int removeByCallsign(const CCallsign &callsign); + int removeByCallsign(const CCallsign &callsign) + { + return this->container().removeIf(&OBJ::getCallsign, callsign); + } //! Remove all objects with callsigns - int removeByCallsigns(const CCallsignSet &callsigns); + int removeByCallsigns(const CCallsignSet &callsigns) + { + return this->container().removeIf([&](const OBJ & obj) { return callsigns.contains(obj.getCallsign()); }); + } //! All suffixes with their respective count //! \remark since using QMap sorted by suffix - QMap getSuffixesAndCount() const; + QMap getSuffixesAndCount() const + { + QMap r; // sorted by key + for (const OBJ &csObj : this->container()) + { + const QString s = csObj.getCallsign().getSuffix(); + if (s.isEmpty()) { continue; } + if (r.contains(s)) + { + r[s] = r[s] + 1; + } + else + { + r.insert(s, 1); + } + } + return r; + } //! All suffixes, in the order of the list //! \remark first found suffixes first - QStringList getSuffixes() const; + QStringList getSuffixes() const + { + QStringList suffixes; + for (const OBJ &csObj : this->container()) + { + const QString s = csObj.getCallsign().getSuffix(); + if (s.isEmpty() || suffixes.contains(s, Qt::CaseInsensitive)) { continue; } + suffixes << s; + } + return suffixes; + } //! Split into 0..n containers as per callsign - QHash splitPerCallsign() const; + QHash splitPerCallsign() const + { + CONTAINER copyContainer(container()); + copyContainer.sortByCallsign(); + QHash result; + CCallsign cs; + for (const OBJ &csObj : copyContainer) + { + if (csObj.getCallsign().isEmpty()) + { + Q_ASSERT(false); // there should be no empty callsigns + continue; + } + if (cs != csObj.getCallsign()) + { + cs = csObj.getCallsign(); + CONTAINER perCallsign({ csObj }); + result.insert(cs, perCallsign); + } + else + { + result[cs].push_back(csObj); + } + } + return result; + } //! Replace or add objects by callsign - int replaceOrAddObjectByCallsign(const OBJ &otherObject); + int replaceOrAddObjectByCallsign(const OBJ &otherObject) + { + const CCallsign cs(otherObject.getCallsign()); + if (cs.isEmpty()) { return 0; } + CONTAINER ©(this->container()); + copy.removeByCallsign(cs); + copy.push_back(otherObject); + return 1; + } //! Replace or add objects by callsign - int replaceOrAddObjectsByCallsign(const CONTAINER &others); + int replaceOrAddObjectsByCallsign(const CONTAINER &others) + { + if (others.isEmpty()) { return 0; } + int c = 0; + CONTAINER copy(this->container()); + for (const OBJ &obj : others) + { + const CCallsign cs(obj.getCallsign()); + if (cs.isEmpty()) { continue; } + copy.removeByCallsign(cs); + copy.push_back(obj); + c++; + } + *this = copy; + return c; + } //! Incremental update or add object - int incrementalUpdateOrAdd(const OBJ &objectBeforeChanged, const CPropertyIndexVariantMap &changedValues); + int incrementalUpdateOrAdd(const OBJ &objectBeforeChanges, const CPropertyIndexVariantMap &changedValues) + { + int c; + const CCallsign cs = objectBeforeChanges.getCallsign(); + if (this->containsCallsign(cs)) + { + if (changedValues.isEmpty()) { return 0; } + c = this->container().applyIf(&OBJ::getCallsign, cs, changedValues); + } + else + { + c = 1; + if (changedValues.isEmpty()) + { + this->container().push_back(objectBeforeChanges); + } + else + { + OBJ objectAdded(objectBeforeChanges); + objectAdded.apply(changedValues); + this->container().push_back(objectAdded); + } + } + return c; + } //! Sort by callsign - void sortByCallsign(); + void sortByCallsign() + { + container().sortBy(&OBJ::getCallsign); + } //! Turn into callsign map - QMap asCallsignMap() const; + QMap asCallsignMap() const + { + QMap map; + for (const OBJ &obj : this->container()) + { + map.insert(obj.getCallsign(), obj); + } + return map; + } //! Turn into callsign hash - QHash asCallsignHash() const; + QHash asCallsignHash() const + { + QHash hash; + for (const OBJ &obj : this->container()) + { + if (obj.getCallsign().isEmpty()) { continue; } + hash.insert(obj.getCallsign(), obj); + } + return hash; + } //! Copy of list sorted by callsign - CONTAINER sortedByCallsign() const; + CONTAINER sortedByCallsign() const + { + CONTAINER copy(this->container()); + copy.sortByCallsign(); + return copy; + } protected: //! Constructor - ICallsignObjectList(); + ICallsignObjectList() + { } //! Container - const CONTAINER &container() const; + const CONTAINER &container() const + { + return static_cast(*this); + } //! Container - CONTAINER &container(); + CONTAINER &container() + { + return static_cast(*this); + } }; - - //! \cond PRIVATE - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ICallsignObjectList; - //! \endcond - } //namespace } // namespace diff --git a/src/blackmisc/db/datastoreobjectlist.cpp b/src/blackmisc/db/datastoreobjectlist.cpp deleted file mode 100644 index b52ef4689..000000000 --- a/src/blackmisc/db/datastoreobjectlist.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* 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. - */ - -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/distributorlist.h" -#include "blackmisc/aviation/airportlist.h" -#include "blackmisc/aviation/liverylist.h" -#include "blackmisc/aviation/aircrafticaocodelist.h" -#include "blackmisc/aviation/aircraftcategorylist.h" -#include "blackmisc/aviation/airlineicaocodelist.h" -#include "blackmisc/db/datastoreobjectlist.h" -#include "blackmisc/db/dbinfolist.h" -#include "blackmisc/db/artifactlist.h" -#include "blackmisc/db/distributionlist.h" -#include "blackmisc/predicates.h" -#include "blackmisc/countrylist.h" -#include "blackmisc/json.h" - -#include -#include -#include -#include -#include - -namespace BlackMisc -{ - namespace Db - { - template - IDatastoreObjectList::IDatastoreObjectList() - { - constexpr bool hasIntegerKey = std::is_base_of::value && std::is_same::value; - constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; - static_assert(hasIntegerKey || hasStringKey, "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType"); - } - - template - OBJ IDatastoreObjectList::findByKey(KEYTYPE key, const OBJ ¬Found) const - { - return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); - } - - template - CONTAINER IDatastoreObjectList::findByKeys(const QSet &keys) const - { - CONTAINER objects; - if (keys.isEmpty()) { return objects; } - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!keys.contains(obj.getDbKey())) { continue; } - objects.push_back(obj); - } - return objects; - } - - template - CONTAINER IDatastoreObjectList::findObjectsWithDbKey() const - { - CONTAINER objects; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - objects.push_back(obj); - } - return objects; - } - - template - CONTAINER IDatastoreObjectList::findObjectsWithoutDbKey() const - { - CONTAINER objects; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (obj.hasValidDbKey()) { continue; } - objects.push_back(obj); - } - return objects; - } - - template - OBJ IDatastoreObjectList::maxKeyObject() const - { - if (this->container().isEmpty()) { return OBJ(); } - const OBJ max = *std::max_element(this->container().begin(), this->container().end(), [](const OBJ & obj1, const OBJ & obj2) - { - bool v1 = obj1.hasValidDbKey(); - bool v2 = obj2.hasValidDbKey(); - if (v1 && v2) - { - return obj1.getDbKey() < obj2.getDbKey(); - } - return v2; - }); - return max; - } - - template - void IDatastoreObjectList::sortByKey() - { - this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getDbKey)); - } - - template - QSet IDatastoreObjectList::toDbKeySet() const - { - QSet keys; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - keys.insert(obj.getDbKey()); - } - return keys; - } - - template - QMap IDatastoreObjectList::toDbKeyValueMap() const - { - QMap map; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - map.insert(obj.getDbKey(), obj); - } - return map; - } - - template - QSet IDatastoreObjectList::toDbKeyStringSet() const - { - QSet keys; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - keys.insert(obj.getDbKeyAsString()); - } - return keys; - } - - template - QString IDatastoreObjectList::dbKeysAsString(const QString &separator) const - { - if (ITimestampObjectList::container().isEmpty()) { return {}; } - const QSet keys = IDatastoreObjectList::toDbKeyStringSet(); - QString s; - for (const QString &k : keys) - { - if (s.isEmpty()) - { - s += k; - } - else - { - s += separator + k; - } - } - return s; - } - - template - KEYTYPE IDatastoreObjectList::getMaxKey(bool *ok) const - { - QSet keys(this->toDbKeySet()); - if (keys.isEmpty()) - { - if (ok) { *ok = false; } - return KEYTYPE(); - } - KEYTYPE max = *std::max_element(keys.begin(), keys.end()); - if (ok) { *ok = true; } - return max; - } - - template - int IDatastoreObjectList::removeObjectsWithKeys(const QSet &keys) - { - if (keys.isEmpty()) { return 0; } - if (this->container().isEmpty()) { return 0; } - CONTAINER newValues; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (keys.contains(obj.getDbKey())) { continue; } - newValues.push_back(obj); - } - const int delta = this->container().size() - newValues.size(); - if (delta > 0) { this->container() = newValues; } - return delta; - } - - template - int IDatastoreObjectList::removeObjectsWithoutDbKey() - { - if (this->container().isEmpty()) { return 0; } - CONTAINER newValues; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - newValues.push_back(obj); - } - int delta = this->container().size() - newValues.size(); - this->container() = newValues; - return delta; - } - - template - int IDatastoreObjectList::replaceOrAddObjectsByKey(const CONTAINER &container) - { - if (container.isEmpty()) { return 0; } - if (this->container().isEmpty()) - { - this->container() = container; - return this->container().size(); - } - CONTAINER newValues(this->container()); - const QSet keys(container.toDbKeySet()); - newValues.removeObjectsWithKeys(keys); - int removeSize = newValues.size(); // size after removing data - newValues.push_back(container); - this->container() = newValues; - return newValues.size() - removeSize; - } - - template - QDateTime IDatastoreObjectList::oldestDbTimestamp() const - { - CONTAINER objs(this->container()); - objs.removeObjectsWithoutDbKey(); - if (objs.isEmpty()) { return QDateTime(); } - return objs.oldestDbTimestamp(); - } - - template - int IDatastoreObjectList::countWithValidDbKey(bool withKey) const - { - int count = 0; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (obj.hasValidDbKey() && withKey) { count++; } - } - return count; - } - - template - int IDatastoreObjectList::countWithValidDbKey() const - { - return this->countWithValidDbKey(true); - } - - template - bool IDatastoreObjectList::containsAnyObjectWithoutKey() const - { - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { return true; } - } - return false; - } - - template - bool IDatastoreObjectList::containsDbKey(KEYTYPE key) const - { - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - if (obj.getDbKey() == key) { return true; } - } - return false; - } - - template - CONTAINER IDatastoreObjectList::fromMultipleJsonFormats(const QJsonObject &jsonObject) - { - // also accept cache format - if (jsonObject.isEmpty()) - { - const CONTAINER c; - return c; - } - - // cache or settings format? - if (Json::looksLikeSwiftDataObjectJson(jsonObject)) - { - const QJsonObject cacheObj = Json::swiftDataObjectValue(jsonObject); - CONTAINER container; - Private::CValueObjectMetaInfoHelper::convertFromMemoizedJson(cacheObj, container, true, 0); // handles both, memoized or "normal" convertFromJson - return container; - } - - // plain vanilla container, does not match memoized objects - if (Json::looksLikeSwiftContainerJson(jsonObject)) - { - CONTAINER container; - container.convertFromJson(jsonObject); - return container; - } - - // still as type/value pair - if (Json::looksLikeSwiftTypeValuePairJson(jsonObject)) - { - const QJsonObject valueObject = jsonObject.value("value").toObject(); - CONTAINER container; - Private::CValueObjectMetaInfoHelper::convertFromMemoizedJson(valueObject, container, true, 0); // handles both, memoized or "normal" convertFromJson - return container; - } - - // DB format, as array - if (jsonObject.contains("data")) - { - return IDatastoreObjectList::fromDatabaseJson(jsonObject.value("data").toArray()); - } - - // no idea what this is - throw CJsonException("Unsupported JSON format"); - } - - template - CONTAINER IDatastoreObjectList::fromMultipleJsonFormats(const QString &jsonString) - { - // also accept cache format - if (jsonString.isEmpty()) - { - const CONTAINER c; - return c; - } - - const QJsonObject jo = Json::jsonObjectFromString(jsonString, false); - return IDatastoreObjectList::fromMultipleJsonFormats(jo); - } - - template - QDateTime IDatastoreObjectList::latestDbTimestamp() const - { - CONTAINER objs(this->container()); - objs.removeObjectsWithoutDbKey(); - if (objs.isEmpty()) { return QDateTime(); } - return objs.latestTimestamp(); - } - - template - CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array) - { - CONTAINER container; - for (const QJsonValue &value : array) - { - container.push_back(OBJ::fromDatabaseJson(value.toObject())); - } - return container; - } - - template - CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array, const QString &prefix) - { - CONTAINER container; - for (const QJsonValue &value : array) - { - container.push_back(OBJ::fromDatabaseJson(value.toObject(), prefix)); - } - return container; - } - - // see here for the reason of thess forward instantiations - // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl - //! \cond PRIVATE - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList; - //! \endcond - } // ns -} // ns diff --git a/src/blackmisc/db/datastoreobjectlist.h b/src/blackmisc/db/datastoreobjectlist.h index 1190d6864..67658dbce 100644 --- a/src/blackmisc/db/datastoreobjectlist.h +++ b/src/blackmisc/db/datastoreobjectlist.h @@ -13,6 +13,7 @@ #include "blackmisc/timestampobjectlist.h" #include "blackmisc/jsonexception.h" +#include "blackmisc/db/datastore.h" #include #include #include @@ -26,104 +27,342 @@ namespace BlackMisc //! Such objects should implement \sa ITimestampBased and \sa IDatastoreObjectWithIntegerKey or \sa IDatastoreObjectWithStringKey template class IDatastoreObjectList : public ITimestampObjectList { + static constexpr bool hasIntegerKey = std::is_base_of::value && std::is_same::value; + static constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; + static_assert(hasIntegerKey || hasStringKey, "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType"); + public: //! Object with key, notFound otherwise - OBJ findByKey(KEYTYPE key, const OBJ ¬Found = OBJ()) const; + OBJ findByKey(KEYTYPE key, const OBJ ¬Found = OBJ()) const + { + return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); + } //! Object with key, notFound otherwise - CONTAINER findByKeys(const QSet &keys) const; + CONTAINER findByKeys(const QSet &keys) const + { + CONTAINER objects; + if (keys.isEmpty()) { return objects; } + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!keys.contains(obj.getDbKey())) { continue; } + objects.push_back(obj); + } + return objects; + } //! Objects with DB key - CONTAINER findObjectsWithDbKey() const; + CONTAINER findObjectsWithDbKey() const + { + CONTAINER objects; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + objects.push_back(obj); + } + return objects; + } //! Objects without DB key - CONTAINER findObjectsWithoutDbKey() const; + CONTAINER findObjectsWithoutDbKey() const + { + CONTAINER objects; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (obj.hasValidDbKey()) { continue; } + objects.push_back(obj); + } + return objects; + } //! Object with max.key - OBJ maxKeyObject() const; + OBJ maxKeyObject() const + { + if (this->container().isEmpty()) { return OBJ(); } + const OBJ max = *std::max_element(this->container().begin(), this->container().end(), [](const OBJ & obj1, const OBJ & obj2) + { + bool v1 = obj1.hasValidDbKey(); + bool v2 = obj2.hasValidDbKey(); + if (v1 && v2) + { + return obj1.getDbKey() < obj2.getDbKey(); + } + return v2; + }); + return max; + } //! Sort by timestamp - void sortByKey(); + void sortByKey() + { + this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getDbKey)); + } //! All keys as set - QSet toDbKeySet() const; + QSet toDbKeySet() const + { + QSet keys; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + keys.insert(obj.getDbKey()); + } + return keys; + } //! As map with DB key/object - QMap toDbKeyValueMap() const; + QMap toDbKeyValueMap() const + { + QMap map; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + map.insert(obj.getDbKey(), obj); + } + return map; + } //! All keys as string set (also integer keys will be converted to string) - QSet toDbKeyStringSet() const; + QSet toDbKeyStringSet() const + { + QSet keys; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + keys.insert(obj.getDbKeyAsString()); + } + return keys; + } //! The DB keys as string - QString dbKeysAsString(const QString &separator) const; + QString dbKeysAsString(const QString &separator) const + { + if (ITimestampObjectList::container().isEmpty()) { return {}; } + const QSet keys = IDatastoreObjectList::toDbKeyStringSet(); + QString s; + for (const QString &k : keys) + { + if (s.isEmpty()) + { + s += k; + } + else + { + s += separator + k; + } + } + return s; + } //! Max.key value (making sense with integer key) - KEYTYPE getMaxKey(bool *ok = nullptr) const; + KEYTYPE getMaxKey(bool *ok = nullptr) const + { + QSet keys(this->toDbKeySet()); + if (keys.isEmpty()) + { + if (ok) { *ok = false; } + return KEYTYPE(); + } + KEYTYPE max = *std::max_element(keys.begin(), keys.end()); + if (ok) { *ok = true; } + return max; + } //! Remove objects with keys - int removeObjectsWithKeys(const QSet &keys); + int removeObjectsWithKeys(const QSet &keys) + { + if (keys.isEmpty()) { return 0; } + if (this->container().isEmpty()) { return 0; } + CONTAINER newValues; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (keys.contains(obj.getDbKey())) { continue; } + newValues.push_back(obj); + } + const int delta = this->container().size() - newValues.size(); + if (delta > 0) { this->container() = newValues; } + return delta; + } //! Remove objects without key - int removeObjectsWithoutDbKey(); + int removeObjectsWithoutDbKey() + { + if (this->container().isEmpty()) { return 0; } + CONTAINER newValues; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + newValues.push_back(obj); + } + int delta = this->container().size() - newValues.size(); + this->container() = newValues; + return delta; + } //! Update or insert data (based on DB key) - int replaceOrAddObjectsByKey(const CONTAINER &container); + int replaceOrAddObjectsByKey(const CONTAINER &container) + { + if (container.isEmpty()) { return 0; } + if (this->container().isEmpty()) + { + this->container() = container; + return this->container().size(); + } + CONTAINER newValues(this->container()); + const QSet keys(container.toDbKeySet()); + newValues.removeObjectsWithKeys(keys); + int removeSize = newValues.size(); // size after removing data + newValues.push_back(container); + this->container() = newValues; + return newValues.size() - removeSize; + } //! Latest DB timestamp (means objects with DB key) - QDateTime latestDbTimestamp() const; + QDateTime latestDbTimestamp() const + { + CONTAINER objs(this->container()); + objs.removeObjectsWithoutDbKey(); + if (objs.isEmpty()) { return QDateTime(); } + return objs.latestTimestamp(); + } //! Latest DB timestamp (means objects with DB key) - QDateTime oldestDbTimestamp() const; + QDateTime oldestDbTimestamp() const + { + CONTAINER objs(this->container()); + objs.removeObjectsWithoutDbKey(); + if (objs.isEmpty()) { return QDateTime(); } + return objs.oldestDbTimestamp(); + } //! Number of objects with/without key - int countWithValidDbKey(bool withKey) const; + int countWithValidDbKey(bool withKey) const + { + int count = 0; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (obj.hasValidDbKey() && withKey) { count++; } + } + return count; + } //! Number of entries with valid DB key - int countWithValidDbKey() const; + int countWithValidDbKey() const + { + return this->countWithValidDbKey(true); + } //! Any object without key? - bool containsAnyObjectWithoutKey() const; + bool containsAnyObjectWithoutKey() const + { + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { return true; } + } + return false; + } //! Contains object with key? - bool containsDbKey(KEYTYPE key) const; + bool containsDbKey(KEYTYPE key) const + { + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + if (obj.getDbKey() == key) { return true; } + } + return false; + } //! From multiple JSON formats //! \remark supports native swift C++ format, DB format, and cache format - static CONTAINER fromMultipleJsonFormats(const QJsonObject &jsonObject); + static CONTAINER fromMultipleJsonFormats(const QJsonObject &jsonObject) + { + // also accept cache format + if (jsonObject.isEmpty()) + { + const CONTAINER c; + return c; + } + + // cache or settings format? + if (Json::looksLikeSwiftDataObjectJson(jsonObject)) + { + const QJsonObject cacheObj = Json::swiftDataObjectValue(jsonObject); + CONTAINER container; + Private::CValueObjectMetaInfoHelper::convertFromMemoizedJson(cacheObj, container, true, 0); // handles both, memoized or "normal" convertFromJson + return container; + } + + // plain vanilla container, does not match memoized objects + if (Json::looksLikeSwiftContainerJson(jsonObject)) + { + CONTAINER container; + container.convertFromJson(jsonObject); + return container; + } + + // still as type/value pair + if (Json::looksLikeSwiftTypeValuePairJson(jsonObject)) + { + const QJsonObject valueObject = jsonObject.value("value").toObject(); + CONTAINER container; + Private::CValueObjectMetaInfoHelper::convertFromMemoizedJson(valueObject, container, true, 0); // handles both, memoized or "normal" convertFromJson + return container; + } + + // DB format, as array + if (jsonObject.contains("data")) + { + return IDatastoreObjectList::fromDatabaseJson(jsonObject.value("data").toArray()); + } + + // no idea what this is + throw CJsonException("Unsupported JSON format"); + } //! From multiple JSON formats //! \remark supports native swift C++ format, DB format, and cache format - static CONTAINER fromMultipleJsonFormats(const QString &jsonString); + static CONTAINER fromMultipleJsonFormats(const QString &jsonString) + { + // also accept cache format + if (jsonString.isEmpty()) + { + const CONTAINER c; + return c; + } + + const QJsonObject jo = Json::jsonObjectFromString(jsonString, false); + return IDatastoreObjectList::fromMultipleJsonFormats(jo); + } //! From DB JSON with default prefixes //! \remark Specialized classes might have their own fromDatabaseJson implementation - static CONTAINER fromDatabaseJson(const QJsonArray &array); + static CONTAINER fromDatabaseJson(const QJsonArray &array) + { + CONTAINER container; + for (const QJsonValue &value : array) + { + container.push_back(OBJ::fromDatabaseJson(value.toObject())); + } + return container; + } //! From DB JSON //! \remark Specialized classes might have their own fromDatabaseJson implementation - static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix); + static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix) + { + CONTAINER container; + for (const QJsonValue &value : array) + { + container.push_back(OBJ::fromDatabaseJson(value.toObject(), prefix)); + } + return container; + } protected: //! Constructor - IDatastoreObjectList(); + IDatastoreObjectList() = default; }; - - //! \cond PRIVATE - class CDbInfo; - class CDbInfoList; - - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList; - //! \endcond } // ns } //ns diff --git a/src/blackmisc/geo/geoobjectlist.cpp b/src/blackmisc/geo/geoobjectlist.cpp deleted file mode 100644 index b6ae5c948..000000000 --- a/src/blackmisc/geo/geoobjectlist.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* 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. - */ - -#include "blackmisc/geo/geoobjectlist.h" -#include "blackmisc/geo/geo.h" -#include "blackmisc/geo/coordinategeodetic.h" -#include "blackmisc/aviation/atcstationlist.h" -#include "blackmisc/aviation/airportlist.h" -#include "blackmisc/aviation/atcstationlist.h" -#include "blackmisc/aviation/aircraftsituationlist.h" -#include "blackmisc/simulation/simulatedaircraftlist.h" -#include "blackmisc/simulation/xplane/navdatareference.h" - -using namespace BlackMisc::Aviation; -using namespace BlackMisc::PhysicalQuantities; - -namespace BlackMisc -{ - namespace Geo - { - template - IGeoObjectList::IGeoObjectList() - { } - - template - const CONTAINER &IGeoObjectList::container() const - { - return static_cast(*this); - } - - template - CONTAINER &IGeoObjectList::container() - { - return static_cast(*this); - } - - template - IGeoObjectWithRelativePositionList::IGeoObjectWithRelativePositionList() - { } - - template - CONTAINER IGeoObjectList::findWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const - { - return this->container().findBy([&](const OBJ & geoObj) - { - return calculateGreatCircleDistance(geoObj, coordinate) <= range; - }); - } - - template - CONTAINER IGeoObjectList::findOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const - { - return this->container().findBy([&](const OBJ & geoObj) - { - return calculateGreatCircleDistance(geoObj, coordinate) > range; - }); - } - - template - OBJ IGeoObjectList::findFirstWithinRangeOrDefault(const ICoordinateGeodetic &coordinate, const CLength &range) const - { - return this->container().findFirstByOrDefault([&](const OBJ & geoObj) - { - return calculateGreatCircleDistance(geoObj, coordinate) <= range; - }); - } - - template - CONTAINER IGeoObjectList::findWithGeodeticMSLHeight() const - { - return this->container().findBy(&OBJ::hasMSLGeodeticHeight, true); - } - - template - bool IGeoObjectList::containsObjectInRange(const ICoordinateGeodetic &coordinate, const CLength &range) const - { - return this->container().containsBy([&](const OBJ & geoObj) - { - const CLength d = coordinate.calculateGreatCircleDistance(geoObj); - return d <= range; - }); - } - - template - bool IGeoObjectList::containsObjectOutsideRange(const ICoordinateGeodetic &coordinate, const CLength &range) const - { - return this->container().containsBy([&](const OBJ & geoObj) - { - const CLength d = coordinate.calculateGreatCircleDistance(geoObj); - return d > range; - }); - } - - template - bool IGeoObjectList::containsNullPosition() const - { - return this->container().containsBy([&](const ICoordinateGeodetic & geoObj) - { - return geoObj.isNull(); - }); - } - - template - bool IGeoObjectList::containsNullPositionOrHeight() const - { - return this->container().containsBy([&](const ICoordinateGeodetic & geoObj) - { - return geoObj.isNull() || geoObj.isGeodeticHeightNull(); - }); - } - - template - typename IGeoObjectList::MinMaxAverageHeight IGeoObjectList::findMinMaxAverageHeight() const - { - MinMaxAverageHeight stats{ CAltitude::null(), CAltitude::null(), CAltitude::null(), 0 }; - if (this->container().isEmpty()) { return stats; } // avoid div by zero - int count = 0; - double avgFt = 0; - for (const OBJ &obj : this->container()) - { - if (!obj.hasMSLGeodeticHeight()) { continue; } - const CAltitude alt = obj.geodeticHeight(); - if (std::get<0>(stats).isNull() || std::get<0>(stats) > alt) - { - std::get<0>(stats) = alt; // min. - } - if (std::get<1>(stats).isNull() || std::get<1>(stats) < alt) - { - std::get<1>(stats) = alt; //max. - } - avgFt += alt.value(CLengthUnit::ft()); // add up - count++; - } - - if (count > 0) { std::get<2>(stats) = CAltitude(avgFt / count, CAltitude::MeanSeaLevel, CLengthUnit::ft()); } - std::get<3>(stats) = count; - return stats; - } - - template - CAltitude IGeoObjectList::findMaxHeight() const - { - if (this->container().isEmpty()) { return CAltitude::null(); } - CAltitude max = CAltitude::null(); - for (const OBJ &obj : this->container()) - { - if (!obj.hasMSLGeodeticHeight()) { continue; } - const CAltitude alt = obj.geodeticHeight(); - if (max.isNull() || alt > max) { max = alt; } - } - return max; - } - - template - int IGeoObjectList::removeInsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) - { - const int size = this->container().size(); - const CONTAINER copy = this->container().findOutsideRange(coordinate, range); - const int d = size - copy.size(); - if (d > 0) { *this = copy; } - return d; - } - - template - int IGeoObjectList::removeOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) - { - const int size = this->container().size(); - const CONTAINER copy = this->container().findWithinRange(coordinate, range); - const int d = size - copy.size(); - if (d > 0) { *this = copy; } - return d; - } - - template - int IGeoObjectList::removeWithoutGeodeticHeight() - { - const int size = this->container().size(); - const CONTAINER copy = this->findWithGeodeticMSLHeight(); - const int d = size - copy.size(); - if (d > 0) { *this = copy; } - return d; - } - - template - CONTAINER IGeoObjectList::findClosest(int number, const ICoordinateGeodetic &coordinate) const - { - CONTAINER closest = this->container().partiallySorted(number, [ & ](const OBJ & a, const OBJ & b) - { - return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); - }); - closest.truncate(number); - return closest; - } - - template - CONTAINER IGeoObjectList::findFarthest(int number, const ICoordinateGeodetic &coordinate) const - { - CONTAINER farthest = this->container().partiallySorted(number, [ & ](const OBJ & a, const OBJ & b) - { - return calculateEuclideanDistanceSquared(a, coordinate) > calculateEuclideanDistanceSquared(b, coordinate); - }); - farthest.truncate(number); - return farthest; - } - - template - OBJ IGeoObjectList::findClosestWithinRange(const ICoordinateGeodetic &coordinate, const CLength &range) const - { - OBJ closest; - CLength distance = CLength::null(); - for (const OBJ &obj : this->container()) - { - const CLength d = coordinate.calculateGreatCircleDistance(obj); - if (d > range) { continue; } - if (distance.isNull() || distance > d) - { - distance = d; - closest = obj; - } - } - return closest; - } - - template - void IGeoObjectList::sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) - { - this->container().sort([ & ](const OBJ & a, const OBJ & b) - { - return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); - }); - } - - template - CONTAINER IGeoObjectList::sortedByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) const - { - CONTAINER copy(this->container()); - copy.sortByEuclideanDistanceSquared(coordinate); - return copy; - } - - template - void IGeoObjectWithRelativePositionList::calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position) - { - for (OBJ &geoObj : this->container()) - { - geoObj.calculcateAndUpdateRelativeDistanceAndBearing(position); - } - } - - template - void IGeoObjectWithRelativePositionList::removeIfOutsideRange(const Geo::ICoordinateGeodetic &position, const CLength &maxDistance, bool updateValues) - { - this->container().removeIf([ & ](OBJ & geoObj) - { - return updateValues ? - geoObj.calculcateAndUpdateRelativeDistanceAndBearing(position) > maxDistance : - geoObj.calculateGreatCircleDistance(position) > maxDistance; - }); - } - - template - void IGeoObjectWithRelativePositionList::sortByRange(const BlackMisc::Geo::ICoordinateGeodetic &position, bool updateValues) - { - if (updateValues) - { - this->calculcateAndUpdateRelativeDistanceAndBearing(position); - } - this->container().sort([ & ](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); - } - - template - void IGeoObjectWithRelativePositionList::sortByDistanceToReferencePosition() - { - this->container().sort([ & ](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); - } - - template - void IGeoObjectWithRelativePositionList::partiallySortByDistanceToReferencePosition(int number) - { - this->container().partiallySort(number, [ & ](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); - } - - template - CONTAINER IGeoObjectWithRelativePositionList::getClosestObjects(int number) const - { - if (number < 1) { return CONTAINER(); } - if (this->container().size() >= number) { return (this->container()); } - CONTAINER closest(this->container()); - closest.partiallySortByDistanceToReferencePosition(number); - Q_ASSERT_X(closest.size() <= number, Q_FUNC_INFO, "size exceeded"); - return closest; - } - - // see here for the reason of thess forward instantiations - // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl - //! \cond PRIVATE - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; - //! \endcond - - } // namespace -} // namespace diff --git a/src/blackmisc/geo/geoobjectlist.h b/src/blackmisc/geo/geoobjectlist.h index 4d7fcb3b7..ef95169e5 100644 --- a/src/blackmisc/geo/geoobjectlist.h +++ b/src/blackmisc/geo/geoobjectlist.h @@ -15,40 +15,15 @@ #include "blackmisc/pq/length.h" #include "blackmisc/blackmiscexport.h" #include "blackmisc/sequence.h" +#include "blackmisc/geo/coordinategeodetic.h" #include #include namespace BlackMisc { - namespace Aviation - { - class CAtcStation; - class CAtcStationList; - class CAirport; - class CAirportList; - class CAircraftSituation; - class CAircraftSituationList; - } - - namespace Simulation - { - class CSimulatedAircraft; - class CSimulatedAircraftList; - - namespace XPlane - { - class CNavDataReference; - class CNavDataReferenceList; - } - } - namespace Geo { - class ICoordinateGeodetic; - class CCoordinateGeodetic; - class CCoordinateGeodeticList; - //! List of objects with geo coordinates. template class IGeoObjectList @@ -60,116 +35,289 @@ namespace BlackMisc //! Find 0..n objects within range of given coordinate //! \param coordinate other position //! \param range within range of other position - CONTAINER findWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + CONTAINER findWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + return this->container().findBy([&](const OBJ & geoObj) + { + return calculateGreatCircleDistance(geoObj, coordinate) <= range; + }); + } //! Find 0..n objects outside range of given coordinate //! \param coordinate other position //! \param range outside range of other position - CONTAINER findOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + CONTAINER findOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + return this->container().findBy([&](const OBJ & geoObj) + { + return calculateGreatCircleDistance(geoObj, coordinate) > range; + }); + } //! Find first in range - OBJ findFirstWithinRangeOrDefault(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + OBJ findFirstWithinRangeOrDefault(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + return this->container().findFirstByOrDefault([&](const OBJ & geoObj) + { + return calculateGreatCircleDistance(geoObj, coordinate) <= range; + }); + } //! Elements with geodetic height (only MSL) - CONTAINER findWithGeodeticMSLHeight() const; + CONTAINER findWithGeodeticMSLHeight() const + { + return this->container().findBy(&OBJ::hasMSLGeodeticHeight, true); + } //! Any object in range? - bool containsObjectInRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + bool containsObjectInRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + return this->container().containsBy([&](const OBJ & geoObj) + { + const PhysicalQuantities::CLength d = coordinate.calculateGreatCircleDistance(geoObj); + return d <= range; + }); + } //! Any object in range? - bool containsObjectOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + bool containsObjectOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + return this->container().containsBy([&](const OBJ & geoObj) + { + const PhysicalQuantities::CLength d = coordinate.calculateGreatCircleDistance(geoObj); + return d > range; + }); + } //! Any NULL position? - bool containsNullPosition() const; + bool containsNullPosition() const + { + return this->container().containsBy([&](const ICoordinateGeodetic & geoObj) + { + return geoObj.isNull(); + }); + } //! Any NULL position or NULL height - bool containsNullPositionOrHeight() const; + bool containsNullPositionOrHeight() const + { + return this->container().containsBy([&](const ICoordinateGeodetic & geoObj) + { + return geoObj.isNull() || geoObj.isGeodeticHeightNull(); + }); + } //! Find min/max/average height - MinMaxAverageHeight findMinMaxAverageHeight() const; + MinMaxAverageHeight findMinMaxAverageHeight() const + { + MinMaxAverageHeight stats{ Aviation::CAltitude::null(), Aviation::CAltitude::null(), Aviation::CAltitude::null(), 0 }; + if (this->container().isEmpty()) { return stats; } // avoid div by zero + int count = 0; + double avgFt = 0; + for (const OBJ &obj : this->container()) + { + if (!obj.hasMSLGeodeticHeight()) { continue; } + const Aviation::CAltitude alt = obj.geodeticHeight(); + if (std::get<0>(stats).isNull() || std::get<0>(stats) > alt) + { + std::get<0>(stats) = alt; // min. + } + if (std::get<1>(stats).isNull() || std::get<1>(stats) < alt) + { + std::get<1>(stats) = alt; //max. + } + avgFt += alt.value(PhysicalQuantities::CLengthUnit::ft()); // add up + count++; + } + + if (count > 0) { std::get<2>(stats) = Aviation::CAltitude(avgFt / count, Aviation::CAltitude::MeanSeaLevel, PhysicalQuantities::CLengthUnit::ft()); } + std::get<3>(stats) = count; + return stats; + } //! Find min/max/average height - Aviation::CAltitude findMaxHeight() const; + Aviation::CAltitude findMaxHeight() const + { + if (this->container().isEmpty()) { return Aviation::CAltitude::null(); } + Aviation::CAltitude max = Aviation::CAltitude::null(); + for (const OBJ &obj : this->container()) + { + if (!obj.hasMSLGeodeticHeight()) { continue; } + const Aviation::CAltitude alt = obj.geodeticHeight(); + if (max.isNull() || alt > max) { max = alt; } + } + return max; + } //! Remove inside range - int removeInsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range); + int removeInsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) + { + const int size = this->container().size(); + const CONTAINER copy = this->container().findOutsideRange(coordinate, range); + const int d = size - copy.size(); + if (d > 0) { *this = copy; } + return d; + } //! Remove outside range - int removeOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range); + int removeOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) + { + const int size = this->container().size(); + const CONTAINER copy = this->container().findWithinRange(coordinate, range); + const int d = size - copy.size(); + if (d > 0) { *this = copy; } + return d; + } //! Remove if there is no geodetic height - int removeWithoutGeodeticHeight(); + int removeWithoutGeodeticHeight() + { + const int size = this->container().size(); + const CONTAINER copy = this->findWithGeodeticMSLHeight(); + const int d = size - copy.size(); + if (d > 0) { *this = copy; } + return d; + } //! Find 0..n objects closest to the given coordinate. - CONTAINER findClosest(int number, const ICoordinateGeodetic &coordinate) const; + CONTAINER findClosest(int number, const ICoordinateGeodetic &coordinate) const + { + CONTAINER closest = this->container().partiallySorted(number, [&](const OBJ & a, const OBJ & b) + { + return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); + }); + closest.truncate(number); + return closest; + } //! Find 0..n objects farthest to the given coordinate. - CONTAINER findFarthest(int number, const ICoordinateGeodetic &coordinate) const; + CONTAINER findFarthest(int number, const ICoordinateGeodetic &coordinate) const + { + CONTAINER farthest = this->container().partiallySorted(number, [&](const OBJ & a, const OBJ & b) + { + return calculateEuclideanDistanceSquared(a, coordinate) > calculateEuclideanDistanceSquared(b, coordinate); + }); + farthest.truncate(number); + return farthest; + } //! Find closest within range to the given coordinate - OBJ findClosestWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + OBJ findClosestWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const + { + OBJ closest; + PhysicalQuantities::CLength distance = PhysicalQuantities::CLength::null(); + for (const OBJ &obj : this->container()) + { + const PhysicalQuantities::CLength d = coordinate.calculateGreatCircleDistance(obj); + if (d > range) { continue; } + if (distance.isNull() || distance > d) + { + distance = d; + closest = obj; + } + } + return closest; + } //! Sort by distance - void sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate); + void sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) + { + this->container().sort([&](const OBJ & a, const OBJ & b) + { + return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); + }); + } //! Sorted by distance - CONTAINER sortedByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) const; + CONTAINER sortedByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) const + { + CONTAINER copy(this->container()); + copy.sortByEuclideanDistanceSquared(coordinate); + return copy; + } protected: //! Constructor - IGeoObjectList(); + IGeoObjectList() + { } //! Container - const CONTAINER &container() const; + const CONTAINER &container() const + { + return static_cast(*this); + } //! Container - CONTAINER &container(); + CONTAINER &container() + { + return static_cast(*this); + } }; - //! \cond PRIVATE - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; - //! \endcond - //! List of objects with geo coordinates. template class IGeoObjectWithRelativePositionList : public IGeoObjectList { public: //! Calculate distances, then sort by range - void sortByRange(const ICoordinateGeodetic &position, bool updateValues); + void sortByRange(const ICoordinateGeodetic &position, bool updateValues) + { + if (updateValues) + { + this->calculcateAndUpdateRelativeDistanceAndBearing(position); + } + this->container().sort([&](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); + } //! If distance is already set, just sort container //! \remark requires calculcateAndUpdateRelativeDistanceAndBearing - void sortByDistanceToReferencePosition(); + void sortByDistanceToReferencePosition() + { + this->container().sort([&](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); + } //! Sort the first n closest objects - void partiallySortByDistanceToReferencePosition(int number); + void partiallySortByDistanceToReferencePosition(int number) + { + this->container().partiallySort(number, [&](const OBJ & a, const OBJ & b) { return a.getRelativeDistance() < b.getRelativeDistance(); }); + } //! Get n closest objects - CONTAINER getClosestObjects(int number) const; + CONTAINER getClosestObjects(int number) const + { + if (number < 1) { return CONTAINER(); } + if (this->container().size() >= number) { return (this->container()); } + CONTAINER closest(this->container()); + closest.partiallySortByDistanceToReferencePosition(number); + Q_ASSERT_X(closest.size() <= number, Q_FUNC_INFO, "size exceeded"); + return closest; + } //! Calculate distances, remove if outside range - void removeIfOutsideRange(const ICoordinateGeodetic &position, const PhysicalQuantities::CLength &maxDistance, bool updateValues); + void removeIfOutsideRange(const ICoordinateGeodetic &position, const PhysicalQuantities::CLength &maxDistance, bool updateValues) + { + this->container().removeIf([&](OBJ & geoObj) + { + return updateValues ? + geoObj.calculcateAndUpdateRelativeDistanceAndBearing(position) > maxDistance : + geoObj.calculateGreatCircleDistance(position) > maxDistance; + }); + } //! Calculate distances - void calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position); + void calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position) + { + for (OBJ &geoObj : this->container()) + { + geoObj.calculcateAndUpdateRelativeDistanceAndBearing(position); + } + } protected: //! Constructor - IGeoObjectWithRelativePositionList(); + IGeoObjectWithRelativePositionList() + { } }; - - //! \cond PRIVATE - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectWithRelativePositionList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectWithRelativePositionList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectWithRelativePositionList; - //! \endcond - } //namespace } // namespace diff --git a/src/blackmisc/network/urllog.cpp b/src/blackmisc/network/urllog.cpp index 61bbf8909..1b24010c2 100644 --- a/src/blackmisc/network/urllog.cpp +++ b/src/blackmisc/network/urllog.cpp @@ -17,8 +17,6 @@ #include -using namespace BlackMisc::Db; - namespace BlackMisc { namespace Network diff --git a/src/blackmisc/orderablelist.cpp b/src/blackmisc/orderablelist.cpp deleted file mode 100644 index 1ca90100d..000000000 --- a/src/blackmisc/orderablelist.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright (C) 2016 - * 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. - */ - -#include "blackmisc/orderablelist.h" -#include "blackmisc/predicates.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/distributorlist.h" -#include "blackmisc/statusmessagelist.h" - -#include -#include - -namespace BlackMisc -{ - template - IOrderableList::IOrderableList() - { - static_assert(std::is_base_of::value, "OBJ needs to implement IOrderable"); - } - - template - const CONTAINER &IOrderableList::container() const - { - return static_cast(*this); - } - - template - CONTAINER &IOrderableList::container() - { - return static_cast(*this); - } - - template - void IOrderableList::sortDescendingByOrder() - { - IOrderableList::container().sortAscendingByOrder(); - std::reverse(this->container().begin(), this->container().end()); - } - - template - void IOrderableList::sortAscendingByOrder() - { - IOrderableList::container().sort(Predicates::MemberLess(&OBJ::getOrder)); - } - - template - void IOrderableList::resetOrder(int offset) - { - int c = offset; - for (OBJ &obj : container()) - { - obj.setOrder(c++); - } - } - - template - bool IOrderableList::needsOrder() const - { - for (const OBJ &obj : container()) - { - if (!obj.hasValidOrder()) { return true; } - } - return false; - } - - template - QList IOrderableList::orderValues() const - { - QList orders; - for (const OBJ &obj : container()) - { - if (!obj.hasValidOrder()) { continue; } - orders.append(obj.getOrder()); - } - return orders; - } - - template - CONTAINER IOrderableList::withoutItemsOfSameOrder(const CONTAINER &items) const - { - const QList orders = items.orderValues(); - if (orders.isEmpty()) { return this->container(); } - - CONTAINER newContainer; - for (const OBJ &obj : container()) - { - if (orders.contains(obj.getOrder())) { continue; } - newContainer.push_back(obj); - } - return newContainer; - } - - template - void IOrderableList::removeItemsWithSameOrder(const CONTAINER &items) - { - const QList orders = items.orderValues(); - if (orders.isEmpty()) { return; } - - CONTAINER newContainer; - for (const OBJ &obj : container()) - { - if (orders.contains(obj.getOrder())) { continue; } - newContainer.push_back(obj); - } - this->container() = newContainer; - } - - template - void IOrderableList::moveTo(const CONTAINER &items, int targetOrder) - { - if (items.isEmpty()) { return; } - CONTAINER newContainer(this->withoutItemsOfSameOrder(items)); // this container without items - CONTAINER newItems(items); - const int shift = items.size(); - newContainer.sortAscendingByOrder(); // sorted by old order - newContainer.resetOrder(); - for (OBJ &obj : newContainer) - { - if (obj.getOrder() < targetOrder) { continue; } - obj.setOrder(obj.getOrder() + shift); - } - newItems.sortAscendingByOrder(); // sort by old order - newItems.resetOrder(targetOrder); - newContainer.push_back(newItems); - this->container() = newContainer; - } - - template - void IOrderableList::freezeOrder() - { - int c = 0; - for (OBJ &obj : container()) - { - obj.setOrder(c++); - } - } - - template - void IOrderableList::freezeOrderReverse() - { - int c = this->container().size() - 1; - for (OBJ &obj : container()) - { - obj.setOrder(c--); - } - } - - template - OBJ IOrderableList::minOrderOrDefault() const - { - if (container().isEmpty()) { return OBJ(); } - OBJ min = container().front(); - for (const OBJ &obj : container()) - { - if (!obj.hasValidOrder()) { continue; } - if (obj.getOrder() < min.getOrder()) - { - min = obj; - } - } - return min; - } - - template - OBJ IOrderableList::maxOrderOrDefault() const - { - if (container().isEmpty()) { return OBJ(); } - OBJ max = container().front(); - for (const OBJ &obj : container()) - { - if (!obj.hasValidOrder()) { continue; } - if (obj.getOrder() > max.getOrder()) - { - max = obj; - } - } - return max; - } - - //! \cond PRIVATE - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IOrderableList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IOrderableList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IOrderableList; - //! \endcond - -} // namespace diff --git a/src/blackmisc/orderablelist.h b/src/blackmisc/orderablelist.h index e3c77345e..26b55b0ee 100644 --- a/src/blackmisc/orderablelist.h +++ b/src/blackmisc/orderablelist.h @@ -21,71 +21,172 @@ namespace BlackMisc template class IOrderableList { + static_assert(std::is_base_of::value, "OBJ needs to implement IOrderable"); + public: //! Sort ascending - void sortAscendingByOrder(); + void sortAscendingByOrder() + { + this->container().sort(Predicates::MemberLess(&OBJ::getOrder)); + } //! Sort descending - void sortDescendingByOrder(); + void sortDescendingByOrder() + { + this->container().sortAscendingByOrder(); + std::reverse(this->container().begin(), this->container().end()); + } //! Set order member to current order - void resetOrder(int offset = 0); + void resetOrder(int offset = 0) + { + int c = offset; + for (OBJ &obj : container()) + { + obj.setOrder(c++); + } + } //! All order values set or missing some? - bool needsOrder() const; + bool needsOrder() const + { + for (const OBJ &obj : container()) + { + if (!obj.hasValidOrder()) { return true; } + } + return false; + } //! All order values IOrderable::order - QList orderValues() const; + QList orderValues() const + { + QList orders; + for (const OBJ &obj : container()) + { + if (!obj.hasValidOrder()) { continue; } + orders.append(obj.getOrder()); + } + return orders; + } //! Items with order will not be included - CONTAINER withoutItemsOfSameOrder(const CONTAINER &items) const; + CONTAINER withoutItemsOfSameOrder(const CONTAINER &items) const + { + const QList orders = items.orderValues(); + if (orders.isEmpty()) { return this->container(); } + + CONTAINER newContainer; + for (const OBJ &obj : container()) + { + if (orders.contains(obj.getOrder())) { continue; } + newContainer.push_back(obj); + } + return newContainer; + } //! Remove the items based on their order IOrderable::order - void removeItemsWithSameOrder(const CONTAINER &items); + void removeItemsWithSameOrder(const CONTAINER &items) + { + const QList orders = items.orderValues(); + if (orders.isEmpty()) { return; } + + CONTAINER newContainer; + for (const OBJ &obj : container()) + { + if (orders.contains(obj.getOrder())) { continue; } + newContainer.push_back(obj); + } + this->container() = newContainer; + } //! Move items to given order - void moveTo(const CONTAINER &items, int targetOrder); + void moveTo(const CONTAINER &items, int targetOrder) + { + if (items.isEmpty()) { return; } + CONTAINER newContainer(this->withoutItemsOfSameOrder(items)); // this container without items + CONTAINER newItems(items); + const int shift = items.size(); + newContainer.sortAscendingByOrder(); // sorted by old order + newContainer.resetOrder(); + for (OBJ &obj : newContainer) + { + if (obj.getOrder() < targetOrder) { continue; } + obj.setOrder(obj.getOrder() + shift); + } + newItems.sortAscendingByOrder(); // sort by old order + newItems.resetOrder(targetOrder); + newContainer.push_back(newItems); + this->container() = newContainer; + } //! Current order of list will be new order values - void freezeOrder(); + void freezeOrder() + { + int c = 0; + for (OBJ &obj : container()) + { + obj.setOrder(c++); + } + } //! Current reverse order of list will be new order values - void freezeOrderReverse(); + void freezeOrderReverse() + { + int c = this->container().size() - 1; + for (OBJ &obj : container()) + { + obj.setOrder(c--); + } + } //! Object with min.order or default - OBJ minOrderOrDefault() const; + OBJ minOrderOrDefault() const + { + if (container().isEmpty()) { return OBJ(); } + OBJ min = container().front(); + for (const OBJ &obj : container()) + { + if (!obj.hasValidOrder()) { continue; } + if (obj.getOrder() < min.getOrder()) + { + min = obj; + } + } + return min; + } //! Object with max.order or default - OBJ maxOrderOrDefault() const; + OBJ maxOrderOrDefault() const + { + if (container().isEmpty()) { return OBJ(); } + OBJ max = container().front(); + for (const OBJ &obj : container()) + { + if (!obj.hasValidOrder()) { continue; } + if (obj.getOrder() > max.getOrder()) + { + max = obj; + } + } + return max; + } protected: //! Constructor - IOrderableList(); + IOrderableList() = default; //! Container - const CONTAINER &container() const; + const CONTAINER &container() const + { + return static_cast(*this); + } //! Container - CONTAINER &container(); + CONTAINER &container() + { + return static_cast(*this); + } }; - - //! \cond PRIVATE - class CStatusMessage; - class CStatusMessageList; - - namespace Simulation - { - class CDistributor; - class CDistributorList; - class CAircraftModel; - class CAircraftModelList; - } - - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IOrderableList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IOrderableList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IOrderableList; - //! \endcond - } //namespace #endif //guard diff --git a/src/blackmisc/simulation/distributorlist.h b/src/blackmisc/simulation/distributorlist.h index 58a31e6d5..253e2f718 100644 --- a/src/blackmisc/simulation/distributorlist.h +++ b/src/blackmisc/simulation/distributorlist.h @@ -26,6 +26,7 @@ namespace BlackMisc { namespace Simulation { + class CAircraftModel; class CSimulatorModel; //! Value object encapsulating a list of distributors. diff --git a/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp b/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp index f4e8a5e78..93d2fe495 100644 --- a/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp +++ b/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp @@ -19,7 +19,6 @@ using namespace BlackMisc; using namespace BlackMisc::Aviation; using namespace BlackMisc::Simulation; -using namespace BlackMisc::Network; namespace BlackMisc { diff --git a/src/blackmisc/simulation/fscommon/aircraftcfgentrieslist.cpp b/src/blackmisc/simulation/fscommon/aircraftcfgentrieslist.cpp index 4649ce944..3b83993ae 100644 --- a/src/blackmisc/simulation/fscommon/aircraftcfgentrieslist.cpp +++ b/src/blackmisc/simulation/fscommon/aircraftcfgentrieslist.cpp @@ -15,7 +15,6 @@ using namespace BlackMisc; using namespace BlackMisc::Simulation; -using namespace BlackMisc::Network; namespace BlackMisc { diff --git a/src/blackmisc/simulation/fscommon/vpilotmodelrule.cpp b/src/blackmisc/simulation/fscommon/vpilotmodelrule.cpp index ed73769cf..541cb9986 100644 --- a/src/blackmisc/simulation/fscommon/vpilotmodelrule.cpp +++ b/src/blackmisc/simulation/fscommon/vpilotmodelrule.cpp @@ -15,7 +15,6 @@ #include -using namespace BlackMisc::Network; using namespace BlackMisc::Aviation; using namespace BlackMisc::Simulation; diff --git a/src/blackmisc/simulation/fscommon/vpilotmodelruleset.cpp b/src/blackmisc/simulation/fscommon/vpilotmodelruleset.cpp index 9da0e23c3..930606186 100644 --- a/src/blackmisc/simulation/fscommon/vpilotmodelruleset.cpp +++ b/src/blackmisc/simulation/fscommon/vpilotmodelruleset.cpp @@ -16,8 +16,6 @@ #include #include -using namespace BlackMisc::Network; - namespace BlackMisc { namespace Simulation diff --git a/src/blackmisc/simulation/fscommon/vpilotrulesreader.cpp b/src/blackmisc/simulation/fscommon/vpilotrulesreader.cpp index b8c2c5fca..dd6eb4e7e 100644 --- a/src/blackmisc/simulation/fscommon/vpilotrulesreader.cpp +++ b/src/blackmisc/simulation/fscommon/vpilotrulesreader.cpp @@ -32,7 +32,6 @@ #include using namespace BlackMisc; -using namespace BlackMisc::Network; namespace BlackMisc { diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp deleted file mode 100644 index 7de3511f7..000000000 --- a/src/blackmisc/timestampobjectlist.cpp +++ /dev/null @@ -1,775 +0,0 @@ -/* 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/timestampobjectlist.h" -#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/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 ITimestampWithOffsetObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampWithOffsetObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampWithOffsetObjectList; - -} // namespace - -//! \endcond diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 410d46a99..6df815309 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -13,6 +13,9 @@ #include "blackmisc/timestampbased.h" #include "blackmisc/blackmiscexport.h" +#include "blackmisc/predicates.h" +#include "blackmisc/verify.h" +#include "blackconfig/buildconfig.h" #include #include @@ -41,6 +44,8 @@ namespace BlackMisc //! Such objects should implement \sa ITimestampBased template class ITimestampObjectList { + static_assert(std::is_base_of::value, "OBJ needs to implement ITimestampBased"); + public: //! Hint if the list is sorted enum HintTimestampSort @@ -51,126 +56,393 @@ namespace BlackMisc }; //! List of objects before dateTime (older) - CONTAINER findBefore(const QDateTime &dateTime) const; + CONTAINER findBefore(const QDateTime &dateTime) const + { + return this->findBefore(dateTime.toMSecsSinceEpoch()); + } //! List of objects before msSinceEpoch (older) - CONTAINER findBefore(qint64 msSinceEpoch) const; + CONTAINER findBefore(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const OBJ & obj) + { + return obj.isOlderThan(msSinceEpoch); + }); + } //! Object before timestamp or default (older) - OBJ findObjectBeforeOrDefault(qint64 msSinceEpoch) const; + OBJ findObjectBeforeOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER before = this->findBefore(msSinceEpoch); + if (before.isEmpty()) { return OBJ(); } + return before.latestObject(); + } //! Get objects before msSinceEpoch and remove those - CONTAINER findBeforeAndRemove(qint64 msSinceEpoch); + CONTAINER findBeforeAndRemove(qint64 msSinceEpoch) + { + CONTAINER result(findBefore(msSinceEpoch)); + this->removeBefore(msSinceEpoch); + return result; + } //! List of objects before now - offset - CONTAINER findBeforeNowMinusOffset(qint64 msOffset) const; + CONTAINER findBeforeNowMinusOffset(qint64 msOffset) const + { + return this->findBefore(QDateTime::currentMSecsSinceEpoch() - msOffset); + } //! List of objects after dateTime (newer) - CONTAINER findAfter(const QDateTime &dateTime) const; + CONTAINER findAfter(const QDateTime &dateTime) const + { + return this->findAfter(dateTime.toMSecsSinceEpoch()); + } //! List of objects after msSinceEpoch (newer) - CONTAINER findAfter(qint64 msSinceEpoch) const; + CONTAINER findAfter(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const OBJ & obj) + { + return obj.isNewerThan(msSinceEpoch); + }); + } //! List of objects before now - offset - CONTAINER findAfterNowMinusOffset(qint64 msOffset) const; + CONTAINER findAfterNowMinusOffset(qint64 msOffset) const + { + return this->findAfter(QDateTime::currentMSecsSinceEpoch() - msOffset); + } //! List of objects after msSinceEpoch (newer) - OBJ findObjectAfterOrDefault(qint64 msSinceEpoch) const; + OBJ findObjectAfterOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER after = this->findAfter(msSinceEpoch); + if (after.isEmpty()) { return OBJ(); } + return after.oldestObject(); + } //! Objects without valid timestamp - CONTAINER findInvalidTimestamps() const; + CONTAINER findInvalidTimestamps() const + { + return this->container().findBy([&](const OBJ & obj) + { + return !obj.hasValidTimestamp(); + }); + } //! Find closest (or default) - OBJ findClosestTimeDistance(qint64 msSinceEpoch) const; + OBJ 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; + } //! Has invalid timestamp - bool hasInvalidTimestamps() const; + bool hasInvalidTimestamps() const + { + return this->container().contains(&OBJ::hasValidTimestamp, false); + } //! Set all timestamps to now - void setCurrentUtcTime(); + void setCurrentUtcTime() + { + for (ITimestampBased &tsObj : this->container()) + { + tsObj.setCurrentUtcTime(); + } + } //! Set all timestamps to given time - void setUtcTime(qint64 msSinceEpoch); + void setUtcTime(qint64 msSinceEpoch) + { + for (ITimestampBased &tsObj : this->container()) + { + tsObj.setMSecsSinceEpoch(msSinceEpoch); + } + } //! Set invalid timestamps to now - void setInvalidTimestampsToCurrentUtcTime(); + void setInvalidTimestampsToCurrentUtcTime() + { + for (ITimestampBased &tsObj : this->container()) + { + if (tsObj.hasValidTimestamp()) { continue; } + tsObj.setCurrentUtcTime(); + } + } //! Latest timestamp - QDateTime latestTimestamp() const; + QDateTime latestTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->latestObject().getUtcTimestamp(); + } //! Latest timestamp - qint64 latestTimestampMsecsSinceEpoch() const; + qint64 latestTimestampMsecsSinceEpoch() const + { + const QDateTime dt(latestTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } //! Oldest timestamp - QDateTime oldestTimestamp() const; + QDateTime oldestTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->oldestObject().getUtcTimestamp(); + } //! Oldest timestamp - qint64 oldestTimestampMsecsSinceEpoch() const; + qint64 oldestTimestampMsecsSinceEpoch() const + { + const QDateTime dt(oldestTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } //! Latest object - OBJ latestObject() const; + OBJ 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; + } //! Latest object - OBJ oldestObject() const; + OBJ 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; + } //! Remove objects with timestamp before dateTime - int removeBefore(const QDateTime &dateTime); + int removeBefore(const QDateTime &dateTime) + { + return this->removeBefore(dateTime.toMSecsSinceEpoch()); + } //! Remove objects with timestamp before dateTime - int removeBefore(qint64 msSinceEpoch); + int removeBefore(qint64 msSinceEpoch) + { + return this->container().removeIf([&](const OBJ & obj) + { + return obj.isOlderThan(msSinceEpoch); + }); + } //! Remove objects older than seconds - int removeOlderThanNowMinusOffset(qint64 offsetMs); + int removeOlderThanNowMinusOffset(qint64 offsetMs) + { + const qint64 epoch = QDateTime::currentMSecsSinceEpoch() - offsetMs; + return this->container().removeIf([&](const OBJ & obj) + { + return obj.isOlderThan(epoch); + }); + } //! Sort by timestamp - void sortLatestFirst(); + void sortLatestFirst() + { + this->container().sortOldestFirst(); + this->container().reverse(); + } //! Sort by timestamp - void sortOldestFirst(); + void sortOldestFirst() + { + this->container().sort(Predicates::MemberLess(&OBJ::getMSecsSinceEpoch)); + } //! Insert as first element by keeping maxElements and the latest first - void push_frontKeepLatestFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); + void push_frontKeepLatestFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1) + { + 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 (BlackConfig::CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(this->isSortedLatestFirst(), Q_FUNC_INFO, "Wrong sort order"); + } + } //! Push back and increase the timestamp at least by +1ms if equal to last element //! \remark if the timestamp is already greater it does not modifcation - void push_backIncreaseTimestamp(const OBJ &newObject); + void 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); + } //! Push back, but set new timestamp - void push_backOverrideTimestamp(const OBJ &newObject, qint64 newTsMsSinceEpoch); + void push_backOverrideTimestamp(const OBJ &newObject, qint64 newTsMsSinceEpoch) + { + OBJ newObjectCopy(newObject); + newObjectCopy.setMSecsSinceEpoch(newTsMsSinceEpoch); + this->container().push_back(newObjectCopy); + } //! Set new timestamps starting with the last element - void setNewTimestampStartingLast(qint64 startTimeStampMs, qint64 deltaTimeMs); + void 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; + } + } //! Replace if an object has the same timestamp - int replaceIfSameTimestamp(const OBJ &newObject); + int replaceIfSameTimestamp(const OBJ &newObject) + { + int c = 0; + for (OBJ &obj : this->container()) + { + if (obj.getMSecsSinceEpoch() == newObject.getMSecsSinceEpoch()) + { + obj = newObject; + c++; + } + } + return c; + } //! Is completely sorted: latest last //! \remark all object must have a valid timestamp - bool isSortedLatestLast() const; + bool 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; + } //! Is completely sorted: latest last //! \remark all object must have a valid timestamp - bool isSortedLatestFirst() const; + bool 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; + } //! Adds a time to all values - void addMsecs(qint64 msToAdd); + void addMsecs(qint64 msToAdd) + { + if (msToAdd == 0) { return; } + for (ITimestampBased &obj : this->container()) + { + obj.addMsecs(msToAdd); + } + } //! Set the hint - void setSortHint(HintTimestampSort hint); + void setSortHint(HintTimestampSort hint) + { + m_tsSortHint = hint; + } //! Difference of timestamp values - //! \cond timestamp list has to be sorted to get meaningful values - MillisecondsMinMaxMean getTimestampDifferenceMinMaxMean() const; + //! \pre timestamp list has to be sorted to get meaningful values + MillisecondsMinMaxMean 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; + } protected: //! Constructor - ITimestampObjectList(); + ITimestampObjectList() = default; //! Container - const CONTAINER &container() const; + const CONTAINER &container() const + { + return static_cast(*this); + } //! Container - CONTAINER &container(); + CONTAINER &container() + { + return static_cast(*this); + } HintTimestampSort m_tsSortHint = NoTimestampSortHint; //!< sort hint }; @@ -179,6 +451,8 @@ namespace BlackMisc //! Such objects should implement \sa ITimestampWithOffsetBased template class ITimestampWithOffsetObjectList : public ITimestampObjectList { + static_assert(std::is_base_of::value, "OBJ needs to implement ITimestampBased"); + public: //! Hint if the list is sorted enum HintAdjustedTimestampSort @@ -188,176 +462,320 @@ namespace BlackMisc }; //! Sort by adjusted timestamp - void sortAdjustedLatestFirst(); + void sortAdjustedLatestFirst() + { + this->container().sortAdjustedOldestFirst(); + this->container().reverse(); + } //! As sorted copy - CONTAINER getSortedAdjustedLatestFirst() const; + CONTAINER getSortedAdjustedLatestFirst() const + { + CONTAINER copy(this->container()); + copy.sortAdjustedLatestFirst(); + return copy; + } //! Get the latest 2 values - CONTAINER getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst = false) const; + CONTAINER getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst = false) const + { + if (this->container().size() < 2) { return CONTAINER(); } + CONTAINER copy(alreadySortedLatestFirst ? this->container() : this->container().getSortedAdjustedLatestFirst()); + copy.truncate(2); + return copy; + } //! Sort by adjusted timestamp - void sortAdjustedOldestFirst(); + void sortAdjustedOldestFirst() + { + this->container().sort(Predicates::MemberLess(&OBJ::getAdjustedMSecsSinceEpoch)); + } //! Any negative or zero offset time? - bool containsZeroOrNegativeOffsetTime() const; + bool containsZeroOrNegativeOffsetTime() const + { + for (const ITimestampWithOffsetBased &obj : this->container()) + { + if (obj.getTimeOffsetMs() <= 0) { return true; } + } + return false; + } //! Any negative offset time? - bool containsNegativeOffsetTime() const; + bool containsNegativeOffsetTime() const + { + for (const ITimestampWithOffsetBased &obj : this->container()) + { + if (obj.getTimeOffsetMs() < 0) { return true; } + } + return false; + } //! Adds a time to all offset values - void addMsecsToOffset(qint64 msToAdd); + void addMsecsToOffset(qint64 msToAdd) + { + for (ITimestampWithOffsetBased &obj : this->container()) + { + obj.addMsecsToOffsetTime(msToAdd); + } + } //! Insert as first element by keeping maxElements and the latest first - void push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); + void push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1) + { + 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(); + } + } //! Insert as first element by keeping maxElements and the latest first //! \remark adjust offset to average offset of two adjacent elements so adjusted values are sorted - void push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); + void push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1) + { + 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 (BlackConfig::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"); + } + } //! Add value, but ignore overlapping (past) values - void push_frontKeepLatestFirstIgnoreOverlapping(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); + void push_frontKeepLatestFirstIgnoreOverlapping(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1) + { + 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); + } //! Prefill with elements - void prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs = -1); + void prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs = -1) + { + this->container().clear(); + const qint64 osTime = value.getTimeOffsetMs(); + const qint64 os = -1 * qAbs(deltaTimeMs < 0 ? osTime : deltaTimeMs); + if (BlackConfig::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); + } //! Is completely sorted: latest last //! \remark all object must have a valid timestamp - bool isSortedAdjustedLatestLast() const; + bool 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; + } //! Is completely sorted: latest last //! \remark all object must have a valid timestamp - bool isSortedAdjustedLatestFirst() const; + bool 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; + } //! List of objects after msSinceEpoch (newer) - CONTAINER findAfterAdjusted(qint64 msSinceEpoch) const; + CONTAINER findAfterAdjusted(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const ITimestampWithOffsetBased & obj) + { + return obj.isNewerThanAdjusted(msSinceEpoch); + }); + } //! List of objects after msSinceEpoch (newer) - OBJ findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const; + OBJ findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER after = this->findAfterAdjusted(msSinceEpoch); + if (after.isEmpty()) { return OBJ(); } + return after.oldestAdjustedObject(); + } //! List of objects before msSinceEpoch (older) - CONTAINER findBeforeAdjusted(qint64 msSinceEpoch) const; + CONTAINER findBeforeAdjusted(qint64 msSinceEpoch) const + { + return this->container().findBy([&](const ITimestampWithOffsetBased & obj) + { + return obj.isOlderThanAdjusted(msSinceEpoch); + }); + } //! Object before timestamp (older) - OBJ findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const; + OBJ findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const + { + const CONTAINER before = this->findBeforeAdjusted(msSinceEpoch); + if (before.isEmpty()) { return OBJ(); } + return before.latestAdjustedObject(); + } //! Closest adjusted time difference - OBJ findClosestTimeDistanceAdjusted(qint64 msSinceEpoch) const; + OBJ 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; + } //! Latest adjusted object - OBJ latestAdjustedObject() const; + OBJ 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; + } //! Oldest adjusted object - OBJ oldestAdjustedObject() const; + OBJ 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; + } //! Latest adjusted timestamp - QDateTime latestAdjustedTimestamp() const; + QDateTime latestAdjustedTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->latestAdjustedObject().getUtcTimestamp(); + } //! Oldest adjusted timestamp - QDateTime oldestAdjustedTimestamp() const; + QDateTime oldestAdjustedTimestamp() const + { + if (this->container().isEmpty()) { return QDateTime(); } + return this->oldestAdjustedObject().getUtcTimestamp(); + } //! Latest adjusted timestamp - qint64 latestAdjustedTimestampMsecsSinceEpoch() const; + qint64 latestAdjustedTimestampMsecsSinceEpoch() const + { + if (this->container().isEmpty()) { return -1; } + const QDateTime dt(this->latestAdjustedTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } //! Oldest adjusted timestamp - qint64 oldestAdjustedTimestampMsecsSinceEpoch() const; + qint64 oldestAdjustedTimestampMsecsSinceEpoch() const + { + if (this->container().isEmpty()) { return -1; } + const QDateTime dt(oldestAdjustedTimestamp()); + return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; + } //! Set the hint - void setAdjustedSortHint(HintAdjustedTimestampSort hint); + void setAdjustedSortHint(HintAdjustedTimestampSort hint) + { + this->container().m_tsAdjustedSortHint = hint; + } //! Difference of timestamp values - //! \cond timestamp list has to be sorted to get meaningful values - MillisecondsMinMaxMean getOffsetMinMaxMean() const; + //! \pre timestamp list has to be sorted to get meaningful values + MillisecondsMinMaxMean 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; + } protected: //! Constructor - ITimestampWithOffsetObjectList(); + ITimestampWithOffsetObjectList() = default; HintAdjustedTimestampSort m_tsAdjustedSortHint = NoAdjustedTimestampSortHint; //!< sort hint }; - - //! \cond PRIVATE - namespace Aviation - { - class CAircraftSituation; - class CAircraftSituationList; - class CAircraftSituationChange; - class CAircraftSituationChangeList; - class CAircraftParts; - class CAircraftPartsList; - class CAirport; - class CAirportList; - class CLivery; - class CLiveryList; - class CAircraftIcaoCode; - class CAircraftIcaoCodeList; - class CAircraftCategory; - class CAircraftCategoryList; - class CAirlineIcaoCode; - class CAirlineIcaoCodeList; - } - - namespace Network - { - class CTextMessage; - class CTextMessageList; - class CRawFsdMessage; - class CRawFsdMessageList; - class CUrlLog; - class CUrlLogList; - } - - namespace Db - { - class CDbInfo; - class CDbInfoList; - class CArtifact; - class CArtifactList; - class CDistribution; - class CDistributionList; - } - - namespace Simulation - { - class CDistributor; - class CDistributorList; - class CAircraftModel; - class CAircraftModelList; - class CMatchingStatistics; - class CMatchingStatisticsEntry; - } - - class CStatusMessage; - class CStatusMessageList; - class CCountry; - class CCountryList; - - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - - // for the derived version both templates are required - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampWithOffsetObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampWithOffsetObjectList; - extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampWithOffsetObjectList; - //! \endcond - } //namespace #endif //guard diff --git a/src/plugins/simulator/emulated/simulatoremulatedmonitordialog.h b/src/plugins/simulator/emulated/simulatoremulatedmonitordialog.h index 72bc11090..d4263f3ee 100644 --- a/src/plugins/simulator/emulated/simulatoremulatedmonitordialog.h +++ b/src/plugins/simulator/emulated/simulatoremulatedmonitordialog.h @@ -12,6 +12,7 @@ #define BLACKSIMPLUGIN_EMULATED_SIMULATOREMULATEDMONITORDIALOG_H #include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/network/textmessage.h" #include "blackmisc/weather/weathergrid.h" #include "blackmisc/statusmessagelist.h" #include "blackmisc/logcategories.h"