From ae247002995ef3ce8313f109356b2282229c8cf3 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 23 Sep 2015 03:20:36 +0200 Subject: [PATCH] refs #452, adjusted aircraft/aviation classes * support for loading from datastore * improved timestamp handling * new color and country classes * new attributes * updates for missing parts in CUser --- src/blackmisc/aviation/aircraftenginelist.cpp | 1 - src/blackmisc/aviation/aircrafticaocode.cpp | 215 +++++++++-- src/blackmisc/aviation/aircrafticaocode.h | 87 ++++- .../aviation/aircrafticaocodelist.cpp | 52 +++ src/blackmisc/aviation/aircrafticaocodelist.h | 20 ++ src/blackmisc/aviation/aircraftsituation.cpp | 2 +- .../aviation/aircraftsituationlist.h | 2 +- src/blackmisc/aviation/airlineicaocode.cpp | 135 +++++-- src/blackmisc/aviation/airlineicaocode.h | 63 +++- .../aviation/airlineicaocodelist.cpp | 30 ++ src/blackmisc/aviation/airlineicaocodelist.h | 13 + src/blackmisc/aviation/airporticaocode.h | 4 +- src/blackmisc/aviation/atcstation.h | 16 +- src/blackmisc/aviation/aviation.h | 1 - src/blackmisc/aviation/livery.cpp | 168 +++++++-- src/blackmisc/aviation/livery.h | 84 ++++- src/blackmisc/aviation/liverylist.cpp | 29 +- src/blackmisc/aviation/liverylist.h | 9 +- src/blackmisc/country.cpp | 151 ++++++++ src/blackmisc/country.h | 104 ++++++ src/blackmisc/countrylist.cpp | 91 +++++ src/blackmisc/countrylist.h | 65 ++++ src/blackmisc/datastore.cpp | 54 ++- src/blackmisc/datastore.h | 35 +- src/blackmisc/datastoreobjectlist.cpp | 19 +- src/blackmisc/datastoreobjectlist.h | 3 + src/blackmisc/datastoreutility.cpp | 66 +++- src/blackmisc/datastoreutility.h | 13 +- src/blackmisc/network/user.cpp | 51 +-- src/blackmisc/network/user.h | 8 +- src/blackmisc/propertyindex.h | 2 + src/blackmisc/rgbcolor.cpp | 233 ++++++++++++ src/blackmisc/rgbcolor.h | 129 +++++++ src/blackmisc/simulation/aircraftmodel.cpp | 193 +++++++--- src/blackmisc/simulation/aircraftmodel.h | 141 ++++++-- .../simulation/aircraftmodellist.cpp | 82 ++++- src/blackmisc/simulation/aircraftmodellist.h | 23 ++ .../simulation/simulatedaircraft.cpp | 338 +++++++++++++++--- src/blackmisc/simulation/simulatedaircraft.h | 312 ++++++++++++++-- .../simulation/simulatedaircraftlist.cpp | 43 ++- .../simulation/simulatedaircraftlist.h | 11 +- src/blackmisc/timestampbased.cpp | 30 +- src/blackmisc/timestampbased.h | 20 +- src/blackmisc/timestampobjectlist.cpp | 6 + src/blackmisc/timestampobjectlist.h | 9 + 45 files changed, 2762 insertions(+), 401 deletions(-) create mode 100644 src/blackmisc/country.cpp create mode 100644 src/blackmisc/country.h create mode 100644 src/blackmisc/countrylist.cpp create mode 100644 src/blackmisc/countrylist.h create mode 100644 src/blackmisc/rgbcolor.cpp create mode 100644 src/blackmisc/rgbcolor.h diff --git a/src/blackmisc/aviation/aircraftenginelist.cpp b/src/blackmisc/aviation/aircraftenginelist.cpp index f9e5c4529..6175ce3c3 100644 --- a/src/blackmisc/aviation/aircraftenginelist.cpp +++ b/src/blackmisc/aviation/aircraftenginelist.cpp @@ -56,7 +56,6 @@ namespace BlackMisc clear(); for (const auto &e : json.keys()) { - CAircraftEngine engine; int number = e.toInt(); engine.convertFromJson(json.value(e).toObject()); diff --git a/src/blackmisc/aviation/aircrafticaocode.cpp b/src/blackmisc/aviation/aircrafticaocode.cpp index b192faf43..fabc9eb28 100644 --- a/src/blackmisc/aviation/aircrafticaocode.cpp +++ b/src/blackmisc/aviation/aircrafticaocode.cpp @@ -8,11 +8,13 @@ */ #include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/aviation/airlineicaocode.h" #include "blackmisc/propertyindex.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/variant.h" #include "blackmisc/datastoreutility.h" #include +#include #include using namespace BlackMisc; @@ -25,20 +27,48 @@ namespace BlackMisc m_designator(designator), m_combinedType(combinedType) {} - CAircraftIcaoCode::CAircraftIcaoCode(const QString &icao, const QString &combinedType, const QString &manufacturer, const QString &model, const QString &wtc, bool realworld, bool legacy, bool military) + CAircraftIcaoCode::CAircraftIcaoCode(const QString &icao, const QString &combinedType, const QString &manufacturer, const QString &model, const QString &wtc, bool realworld, bool legacy, bool military, int rank) : m_designator(icao.trimmed().toUpper()), m_combinedType(combinedType.trimmed().toUpper()), m_manufacturer(manufacturer.trimmed()), - m_modelDescription(model.trimmed()), m_wtc(wtc.trimmed().toUpper()), m_realworld(realworld), m_legacy(legacy), m_military(military) - {} + m_modelDescription(model.trimmed()), m_wtc(wtc.trimmed().toUpper()), m_realWorld(realworld), m_legacy(legacy), m_military(military), m_rank(rank) + { + if (m_rank < 0 || m_rank >= 10) { m_rank = 10; } + } QString CAircraftIcaoCode::convertToQString(bool i18n) const { Q_UNUSED(i18n); QString s(this->m_designator); - if (this->hasCombinedType()) { s.append(" ").append(this->m_combinedType); } + if (this->hasValidCombinedType()) { s.append(" ").append(this->m_combinedType); } if (this->hasValidWtc()) { s.append(" ").append(this->m_wtc); } return s; } + void CAircraftIcaoCode::updateMissingParts(const CAircraftIcaoCode &otherIcaoCode) + { + if (!this->hasDesignator()) { this->setDesignator(otherIcaoCode.getDesignator()); } + if (!this->hasValidWtc()) { this->setWtc(otherIcaoCode.getDesignator()); } + if (!this->hasValidCombinedType()) { this->setCombinedType(otherIcaoCode.getCombinedType()); } + if (this->m_manufacturer.isEmpty()) { this->setManufacturer(otherIcaoCode.getManufacturer());} + if (this->m_modelDescription.isEmpty()) { this->setModelDescription(otherIcaoCode.getModelDescription()); } + if (!this->hasValidDbKey()) + { + this->setDbKey(otherIcaoCode.getDbKey()); + this->setUtcTimestamp(otherIcaoCode.getUtcTimestamp()); + } + } + + CStatusMessageList CAircraftIcaoCode::validate() const + { + static const CLogCategoryList cats( { CLogCategory("swift.blackmisc.aircrafticao"), CLogCategory::validation()}); + CStatusMessageList msg; + if (!hasKnownDesignator()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Aircraft ICAO: unknown designator")); } + if (!hasValidCombinedType()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Aircraft ICAO: invalid combined type")); } + if (!hasValidWtc()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Aircraft ICAO: wrong WTC")); } + if (!hasManufacturer()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Aircraft ICAO: missing manufacturer")); } + if (!hasModelDescription()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Aircraft ICAO: no description")); } + return msg; + } + bool CAircraftIcaoCode::hasDesignator() const { return !this->m_designator.isEmpty(); @@ -49,12 +79,54 @@ namespace BlackMisc return (this->hasDesignator() && this->getDesignator() != "ZZZZ"); } + bool CAircraftIcaoCode::hasValidCombinedType() const + { + return isValidCombinedType(getCombinedType()); + } + QString CAircraftIcaoCode::getEngineType() const { if (this->m_combinedType.length() != 3) return ""; return this->m_combinedType.right(1); } + int CAircraftIcaoCode::getEngineCount() const + { + if (this->m_combinedType.length() < 2) { return -1; } + QString c(this->m_combinedType.mid(1, 1)); + if (c == "-") { return -1; } + bool ok; + int ec = c.toInt(&ok); + if (ok && ec >= 0 && ec < 10) { return ec; } + return -1; + } + + QString CAircraftIcaoCode::getEngineCountString() const + { + if (this->m_combinedType.length() < 2) { return ""; } + return this->m_combinedType.mid(1, 1); + } + + QString CAircraftIcaoCode::getAircraftType() const + { + if (this->m_combinedType.length() < 1) { return ""; } + QString c(this->m_combinedType.left(1)); + if (c == "-") { return ""; } + return c; + } + + QString CAircraftIcaoCode::getDesignatorManufacturer() const + { + QString d(getDesignator()); + if (this->hasManufacturer()) { d = d.append(" ").append(this->getManufacturer());} + return d.trimmed(); + } + + bool CAircraftIcaoCode::hasManufacturer() const + { + return !m_manufacturer.isEmpty(); + } + bool CAircraftIcaoCode::isVtol() const { // special designators @@ -79,9 +151,62 @@ namespace BlackMisc return false; } + void CAircraftIcaoCode::setCodeFlags(bool military, bool legacy, bool realWorld) + { + m_military = military; + m_legacy = legacy; + m_realWorld = realWorld; + } + + void CAircraftIcaoCode::setMilitary(bool military) + { + m_military = military; + } + + void CAircraftIcaoCode::setRealWorld(bool realWorld) + { + m_realWorld = realWorld; + } + + void CAircraftIcaoCode::setLegacy(bool legacy) + { + m_legacy = legacy; + } + + QString CAircraftIcaoCode::getRankString() const + { + return QString::number(getRank()); + } + + void CAircraftIcaoCode::setRank(int rank) + { + if (rank < 0 || rank >= 10) + { + m_rank = 10; + } + else + { + m_rank = rank; + } + } + + QString CAircraftIcaoCode::getCombinedStringWithKey() const + { + QString s(getDesignator()); + if (hasManufacturer()) { s = s.append(" ").append(getManufacturer()); } + if (hasModelDescription()) { s = s.append(" ").append(getModelDescription()); } + return s.append(" ").append(getDbKeyAsStringInParentheses()); + } + bool CAircraftIcaoCode::hasCompleteData() const { - return hasCombinedType() && hasDesignator() && hasValidWtc(); + return hasValidCombinedType() && hasDesignator() && hasValidWtc() && hasManufacturer(); + } + + bool CAircraftIcaoCode::matchesDesignator(const QString &designator) const + { + if (designator.isEmpty()) { return false; } + return designator.trimmed().toUpper() == this->m_designator; } CVariant CAircraftIcaoCode::propertyByIndex(const BlackMisc::CPropertyIndex &index) const @@ -108,7 +233,11 @@ namespace BlackMisc case IndexIsMilitary: return CVariant::fromValue(this->m_military); case IndexIsRealworld: - return CVariant::fromValue(this->m_realworld); + return CVariant::fromValue(this->m_realWorld); + case IndexRank: + return CVariant::fromValue(this->m_rank); + case IndexDesignatorManufacturer: + return CVariant::fromValue(this->getDesignatorManufacturer()); default: return CValueObject::propertyByIndex(index); } @@ -142,6 +271,9 @@ namespace BlackMisc case IndexIsMilitary: this->m_military = variant.toBool(); break; + case IndexRank: + this->m_rank = variant.toInt(); + break; default: CValueObject::setPropertyByIndex(variant, index); break; @@ -162,50 +294,58 @@ namespace BlackMisc { if (combinedType.length() != 3) { return false; } + // Amphibian, Glider, Helicopter, Seaplane, Landplane, Tilt wing + // Electric, Jet, Piston, Turpoprop static QThreadStorage tsRegex; - if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[A-Z][0-9][A-Z]$")); } + if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[AGHSLT][0-9][EJPT]$")); } const QRegularExpression ®exp = tsRegex.localData(); return (regexp.match(combinedType).hasMatch()); } - CAircraftIcaoCode CAircraftIcaoCode::fromDatabaseJson(const QJsonObject &json) + bool CAircraftIcaoCode::isValidWtc(const QString &candidate) { - QJsonArray inner = json["cell"].toArray(); - Q_ASSERT_X(!inner.isEmpty(), Q_FUNC_INFO, "Missing JSON"); - if (inner.isEmpty()) { return CAircraftIcaoCode(); } + if (candidate.isEmpty()) { return true; } // we accept unspecified + if (candidate.length() == 1) + { + return candidate == "L" || candidate == "M" || candidate == "H"; + } + return false; + } - int i = 0; - int dbKey(inner.at(i++).toInt(-1)); - QString designator(inner.at(i++).toString()); - QString manufacturer(inner.at(i++).toString()); - QString model(inner.at(i++).toString()); - QString type(inner.at(i++).toString()); - QString engine(inner.at(i++).toString()); - QString engineCount(inner.at(i++).toString()); + CAircraftIcaoCode CAircraftIcaoCode::fromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + if (!existsKey(json, prefix)) + { + // when using relationship, this can be null + return CAircraftIcaoCode(); + } + + QString designator(json.value(prefix + "designator").toString()); + QString manufacturer(json.value(prefix + "manufacturer").toString()); + QString model(json.value(prefix + "model").toString()); + QString type(json.value(prefix + "type").toString()); + QString engine(json.value(prefix + "engine").toString()); + int engineCount(json.value(prefix + "enginecount").toInt(-1)); QString combined(createdCombinedString(type, engineCount, engine)); - QString wtc(inner.at(i++).toString()); + QString wtc(json.value("wtc").toString()); if (wtc.length() > 1 && wtc.contains("/")) { // "L/M" -> "M" wtc = wtc.right(1); } - bool real = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); - bool legacy = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); - bool military = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); + bool real = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "realworld").toString()); + bool legacy = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "legacy").toString()); + bool military = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "military").toString()); + int rank(json.value("rank").toInt(10)); Q_ASSERT_X(wtc.length() < 2, Q_FUNC_INFO, "WTC too long"); CAircraftIcaoCode code( - designator, - combined, - manufacturer, - model, - wtc, - real, - legacy, - military + designator, combined, + manufacturer, model, wtc, + real, legacy, military, rank ); - code.setDbKey(dbKey); + code.setKeyAndTimestampFromDatabaseJson(json, prefix); return code; } @@ -235,5 +375,16 @@ namespace BlackMisc return c; } + QString CAircraftIcaoCode::createdCombinedString(const QString &type, int engineCount, const QString &engine) + { + if (engineCount >= 0 && engineCount < 10) + { + return createdCombinedString(type, QString::number(engineCount), engine); + } + else + { + return createdCombinedString(type, "", engine); + } + } } // namespace } // namespace diff --git a/src/blackmisc/aviation/aircrafticaocode.h b/src/blackmisc/aviation/aircrafticaocode.h index 03e5b6c9b..558d6b149 100644 --- a/src/blackmisc/aviation/aircrafticaocode.h +++ b/src/blackmisc/aviation/aircrafticaocode.h @@ -17,11 +17,14 @@ #include "blackmisc/propertyindex.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/datastore.h" +#include "blackmisc/statusmessagelist.h" namespace BlackMisc { namespace Aviation { + class CAirlineIcaoCode; + //! Value object for ICAO classification class BLACKMISC_EXPORT CAircraftIcaoCode : public CValueObject, @@ -40,6 +43,8 @@ namespace BlackMisc IndexIsMilitary, IndexIsLegacy, IndexIsVtol, + IndexRank, + IndexDesignatorManufacturer }; //! Default constructor. @@ -50,7 +55,7 @@ namespace BlackMisc //! Constructor CAircraftIcaoCode(const QString &icao, const QString &combinedType, const QString &manufacturer, - const QString &model, const QString &wtc, bool realworld, bool legacy, bool military); + const QString &model, const QString &wtc, bool realworld, bool legacy, bool military, int rank); //! Get ICAO designator, e.g. "B737" const QString &getDesignator() const { return m_designator; } @@ -68,26 +73,44 @@ namespace BlackMisc const QString &getCombinedType() const { return this->m_combinedType; } //! Combined type available? - bool hasCombinedType() const { return this->getCombinedType().length() == 3; } + bool hasValidCombinedType() const; //! Get engine type, e.g. "J" QString getEngineType() const; + //! Engine count if any, -1 if no value is set + int getEngineCount() const; + + //! Engine count as string, if not available "" + QString getEngineCountString() const; + + //! Aircraft type, such a L(andplane), S(eaplane), H(elicopter) + QString getAircraftType() const; + //! Set type void setCombinedType(const QString &type) { this->m_combinedType = type.trimmed().toUpper(); } //! Get model description, e.g. "A-330-200" const QString &getModelDescription() const { return m_modelDescription; } + //! Designator + Manufacturer + QString getDesignatorManufacturer() const; + //! Set the model description void setModelDescription(const QString &modelDescription) { m_modelDescription = modelDescription.trimmed(); } + //! Has model description + bool hasModelDescription() const { return !this->m_modelDescription.isEmpty(); } + //! Get manufacturer, e.g. "Airbus" const QString &getManufacturer() const { return m_manufacturer; } //! Set the manufacturer void setManufacturer(const QString &manufacturer) { m_manufacturer = manufacturer.trimmed(); } + //! Manufacturer + bool hasManufacturer() const; + //! Get WTC const QString &getWtc() const { return m_wtc; } @@ -100,9 +123,45 @@ namespace BlackMisc //! Is VTOL aircraft (helicopter, tilt wing) bool isVtol() const; + //! Military? + bool isMilitary() const { return m_military; } + + //! Real world aircraft? + bool isRealWorld() const { return m_realWorld; } + + //! Legacy aircraft + bool isLegacyAircraft() const { return m_legacy; } + + //! Flags + void setCodeFlags(bool military, bool legacy, bool realWorld); + + //! Military + void setMilitary(bool military); + + //! Real world + void setRealWorld(bool realWorld); + + //! Legacy + void setLegacy(bool legacy); + + //! Ranking + int getRank() const { return m_rank; } + + //! Ranking + QString getRankString() const; + + //! Ranking + void setRank(int rank); + + //! Comined descriptive string with key + QString getCombinedStringWithKey() const; + //! All data set? bool hasCompleteData() const; + //! Matches designator string? + bool matchesDesignator(const QString &designator) const; + //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; @@ -112,14 +171,23 @@ namespace BlackMisc //! \copydoc CValueObject::convertToQString QString convertToQString(bool i18n = false) const; + //! Update missing parts + void updateMissingParts(const CAircraftIcaoCode &otherIcaoCode); + + //! Validate data + BlackMisc::CStatusMessageList validate() const; + //! Valid designator? static bool isValidDesignator(const QString &designator); //! Valid combined type static bool isValidCombinedType(const QString &combinedType); + //! Valid WTC code? + static bool isValidWtc(const QString &candidate); + //! From our database JSON format - static CAircraftIcaoCode fromDatabaseJson(const QJsonObject &json); + static CAircraftIcaoCode fromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); private: BLACK_ENABLE_TUPLE_CONVERSION(CAircraftIcaoCode) @@ -127,14 +195,17 @@ namespace BlackMisc QString m_combinedType; //!< "L2J" QString m_manufacturer; //!< "Airbus" QString m_modelDescription; //!< "A-330-200" - QString m_wtc; //!< wake turbulence "M","H" "L/M", "L" - bool m_realworld = true; //!< real world aircraft + QString m_wtc; //!< wake turbulence "M","H" "L/M", "L", we only use the one letter versions + bool m_realWorld = true; //!< real world aircraft bool m_legacy = false; //!< legacy code bool m_military = false; //!< military aircraft? + int m_rank = 10; //!< rank among same codes //! Create a combined string like L2J static QString createdCombinedString(const QString &type, const QString &engineCount, const QString &engine); + //! Create a combined string like L2J + static QString createdCombinedString(const QString &type, int engineCount, const QString &engine); }; } // namespace } // namespace @@ -142,14 +213,16 @@ namespace BlackMisc Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftIcaoCode) BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Aviation::CAircraftIcaoCode, ( o.m_dbKey, + o.m_timestampMSecsSinceEpoch, o.m_designator, o.m_combinedType, o.m_manufacturer, o.m_modelDescription, o.m_wtc, o.m_military, - o.m_realworld, - o.m_legacy + o.m_realWorld, + o.m_legacy, + o.m_rank )) #endif // guard diff --git a/src/blackmisc/aviation/aircrafticaocodelist.cpp b/src/blackmisc/aviation/aircrafticaocodelist.cpp index 390766190..f2119fe54 100644 --- a/src/blackmisc/aviation/aircrafticaocodelist.cpp +++ b/src/blackmisc/aviation/aircrafticaocodelist.cpp @@ -18,6 +18,58 @@ namespace BlackMisc CSequence(other) { } + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDesignator(const QString &designator) + { + if (!CAircraftIcaoCode::isValidDesignator(designator)) { return CAircraftIcaoCodeList(); } + return this->findBy([&](const CAircraftIcaoCode & code) + { + return code.matchesDesignator(designator); + }); + } + + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByManufacturer(const QString &manufacturer) + { + if (manufacturer.isEmpty()) { return CAircraftIcaoCodeList(); } + return this->findBy([&](const CAircraftIcaoCode & code) + { + return code.getManufacturer().startsWith(manufacturer, Qt::CaseInsensitive); + }); + } + + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDescription(const QString &description) + { + if (description.isEmpty()) { return CAircraftIcaoCodeList(); } + return this->findBy([&](const CAircraftIcaoCode & code) + { + return code.getModelDescription().startsWith(description, Qt::CaseInsensitive); + }); + } + + CAircraftIcaoCode CAircraftIcaoCodeList::findFirstByDesignatorAndRank(const QString &designator) + { + if (!CAircraftIcaoCode::isValidDesignator(designator)) { return CAircraftIcaoCode(); } + CAircraftIcaoCodeList codes(findByDesignator(designator)); + if (codes.isEmpty()) { return CAircraftIcaoCode(); } + if (codes.size() < 2) { return codes.front(); } + codes.sortBy(&CAircraftIcaoCode::getRank, &CAircraftIcaoCode::getDbKey); + return codes.front(); + } + + void CAircraftIcaoCodeList::sortByRank() + { + this->sortBy(&CAircraftIcaoCode::getRank); + } + + QStringList CAircraftIcaoCodeList::toCompleterStrings() const + { + QStringList c; + for (const CAircraftIcaoCode &icao : *this) + { + c.append(icao.getCombinedStringWithKey()); + } + return c; + } + CAircraftIcaoCodeList CAircraftIcaoCodeList::fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete) { CAircraftIcaoCodeList codes; diff --git a/src/blackmisc/aviation/aircrafticaocodelist.h b/src/blackmisc/aviation/aircrafticaocodelist.h index bc702d506..69f04d34b 100644 --- a/src/blackmisc/aviation/aircrafticaocodelist.h +++ b/src/blackmisc/aviation/aircrafticaocodelist.h @@ -14,6 +14,7 @@ #include "aircrafticaocode.h" #include "blackmisc/blackmiscexport.h" +#include "blackmisc/datastoreobjectlist.h" #include "blackmisc/collection.h" #include "blackmisc/sequence.h" #include @@ -25,6 +26,7 @@ namespace BlackMisc //! Value object encapsulating a list of ICAO codes. class BLACKMISC_EXPORT CAircraftIcaoCodeList : public CSequence, + public BlackMisc::IDatastoreObjectListWithIntegerKey, public BlackMisc::Mixin::MetaType { public: @@ -36,6 +38,24 @@ namespace BlackMisc //! Construct from a base class object. CAircraftIcaoCodeList(const CSequence &other); + //! Find by designator + CAircraftIcaoCodeList findByDesignator(const QString &designator); + + //! Find by manufacturer + CAircraftIcaoCodeList findByManufacturer(const QString &manufacturer); + + //! Find by model description + CAircraftIcaoCodeList findByDescription(const QString &description); + + //! Find by designator, then best match by rank + CAircraftIcaoCode findFirstByDesignatorAndRank(const QString &designator); + + //! Sort by rank + void sortByRank(); + + //! For selection completion + QStringList toCompleterStrings() const; + //! From our database JSON format static CAircraftIcaoCodeList fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete = true); }; diff --git a/src/blackmisc/aviation/aircraftsituation.cpp b/src/blackmisc/aviation/aircraftsituation.cpp index c600603b8..d8b719a53 100644 --- a/src/blackmisc/aviation/aircraftsituation.cpp +++ b/src/blackmisc/aviation/aircraftsituation.cpp @@ -41,7 +41,7 @@ namespace BlackMisc s.append(" pitch: ").append(this->m_pitch.toQString(i18n)); s.append(" gs: ").append(this->m_groundspeed.toQString(i18n)); s.append(" heading: ").append(this->m_heading.toQString(i18n)); - s.append(" timestamp: ").append(this->getFormattedUtcTimestamp()); + s.append(" timestamp: ").append(this->getFormattedUtcTimestampDhms()); return s; } diff --git a/src/blackmisc/aviation/aircraftsituationlist.h b/src/blackmisc/aviation/aircraftsituationlist.h index a60dde2f1..ee69e7cd2 100644 --- a/src/blackmisc/aviation/aircraftsituationlist.h +++ b/src/blackmisc/aviation/aircraftsituationlist.h @@ -14,8 +14,8 @@ #include "blackmisc/blackmiscexport.h" #include "blackmisc/aviation/aircraftsituation.h" -#include "blackmisc/timestampobjectlist.h" #include "blackmisc/aviation/callsignobjectlist.h" +#include "blackmisc/timestampobjectlist.h" #include "blackmisc/sequence.h" namespace BlackMisc diff --git a/src/blackmisc/aviation/airlineicaocode.cpp b/src/blackmisc/aviation/airlineicaocode.cpp index 56b637646..78da7ce82 100644 --- a/src/blackmisc/aviation/airlineicaocode.cpp +++ b/src/blackmisc/aviation/airlineicaocode.cpp @@ -13,8 +13,9 @@ #include "blackmisc/variant.h" #include +#include #include - +#include "blackmisc/logmessage.h" using namespace BlackMisc; @@ -22,13 +23,12 @@ namespace BlackMisc { namespace Aviation { - CAirlineIcaoCode::CAirlineIcaoCode(const QString &airlineDesignator) : m_designator(airlineDesignator.trimmed().toUpper()) {} - CAirlineIcaoCode::CAirlineIcaoCode(const QString &airlineDesignator, const QString &airlineName, const QString &countryIso, const QString &country, const QString &telephony, bool virtualAirline) - : m_designator(airlineDesignator.trimmed().toUpper()), m_name(airlineName), m_countryIso(countryIso.trimmed().toUpper()), m_country(country), m_telephonyDesignator(telephony), m_isVa(virtualAirline) + CAirlineIcaoCode::CAirlineIcaoCode(const QString &airlineDesignator, const QString &airlineName, const BlackMisc::CCountry &country, const QString &telephony, bool virtualAirline, bool operating) + : m_designator(airlineDesignator.trimmed().toUpper()), m_name(airlineName), m_country(country), m_telephonyDesignator(telephony), m_isVa(virtualAirline), m_isOperating(operating) {} const QString CAirlineIcaoCode::getVDesignator() const @@ -37,15 +37,60 @@ namespace BlackMisc return QString("V").append(this->m_designator); } + QString CAirlineIcaoCode::getDesignatorNameCountry() const + { + QString s(this->getDesignator()); + if (this->hasName()) { s = s.append(" ").append(this->getName()); } + if (this->hasValidCountry()) { s = s.append(" ").append(this->getCountryIso()); } + return s.trimmed(); + } + + bool CAirlineIcaoCode::hasValidCountry() const + { + return this->m_country.isValid(); + } + + bool CAirlineIcaoCode::hasValidDesignator() const + { + return isValidAirlineDesignator(m_designator); + } + + bool CAirlineIcaoCode::matchesDesignator(const QString &designator) const + { + if (designator.isEmpty()) { return false; } + return designator.trimmed().toUpper() == this->m_designator; + } + + bool CAirlineIcaoCode::matchesVDesignator(const QString &designator) const + { + if (designator.isEmpty()) { return false; } + return designator.trimmed().toUpper() == this->getVDesignator(); + } + bool CAirlineIcaoCode::hasCompleteData() const { - return this->hasDesignator() && this->hasCountryIso() && this->hasName(); + return this->hasValidDesignator() && this->hasValidCountry() && this->hasName(); + } + + CIcon CAirlineIcaoCode::toIcon() const + { + if (this->m_designator.length() > 2) + { + return CIcon("images/airlines/" + m_designator.toLower() + ".png", + this->convertToQString()); + } + else + { + return CIcon::iconByIndex(CIcons::StandardIconEmpty); + } } QString CAirlineIcaoCode::convertToQString(bool i18n) const { Q_UNUSED(i18n); QString s(this->m_designator); + if (this->m_name.isEmpty()) { return ""; } + if (!this->m_name.isEmpty()) { s.append(" (").append(this->m_name).append(")"); } return s; } @@ -59,15 +104,19 @@ namespace BlackMisc case IndexAirlineDesignator: return CVariant::fromValue(this->m_designator); case IndexAirlineCountryIso: - return CVariant::fromValue(this->m_countryIso); + return CVariant::fromValue(this->getCountryIso()); case IndexAirlineCountry: - return CVariant::fromValue(this->m_country); + return this->m_country.propertyByIndex(index.copyFrontRemoved()); case IndexAirlineName: return CVariant::fromValue(this->m_name); case IndexTelephonyDesignator: return CVariant::fromValue(this->m_telephonyDesignator); case IndexIsVirtualAirline: return CVariant::fromValue(this->m_isVa); + case IndexIsOperating: + return CVariant::fromValue(this->m_isOperating); + case IndexDesignatorNameCountry: + return CVariant::fromValue(this->getDesignatorNameCountry()); default: return CValueObject::propertyByIndex(index); } @@ -83,11 +132,8 @@ namespace BlackMisc case IndexAirlineDesignator: this->setDesignator(variant.value()); break; - case IndexAirlineCountryIso: - this->setCountryIso(variant.value()); - break; case IndexAirlineCountry: - this->setCountry(variant.value()); + this->setCountry(variant.value()); break; case IndexAirlineName: this->setName(variant.value()); @@ -98,12 +144,25 @@ namespace BlackMisc case IndexIsVirtualAirline: this->setVirtualAirline(variant.toBool()); break; + case IndexIsOperating: + this->setOperating(variant.toBool()); + break; default: CValueObject::setPropertyByIndex(variant, index); break; } } + CStatusMessageList CAirlineIcaoCode::validate() const + { + static const CLogCategoryList cats( { CLogCategory(this->getClassName()), CLogCategory::validation()}); + CStatusMessageList msgs; + if (!hasValidDesignator()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Airline: missing designator")); } + if (!hasValidCountry()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Airline: missing country")); } + if (!hasName()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Airline: no name")); } + return msgs; + } + bool CAirlineIcaoCode::isValidAirlineDesignator(const QString &airline) { if (airline.length() < 2 || airline.length() > 5) return false; @@ -114,27 +173,47 @@ namespace BlackMisc return (regexp.match(airline).hasMatch()); } - CAirlineIcaoCode CAirlineIcaoCode::fromDatabaseJson(const QJsonObject &json) + QString CAirlineIcaoCode::getCombinedStringWithKey() const { - // https://ubuntu12/vatrep/public/service/allairlineicao.php?rows=10 - QJsonArray inner = json["cell"].toArray(); - Q_ASSERT_X(!inner.isEmpty(), Q_FUNC_INFO, "Missing JSON"); - if (inner.isEmpty()) { return CAirlineIcaoCode(); } + QString s(getVDesignator()); + if (hasName()) { s = s.append(" ").append(getName()); } + return s.append(" ").append(getDbKeyAsStringInParentheses()); + } - int i = 0; - int dbKey = inner.at(i++).toInt(-1); - QString designator(inner.at(i++).toString()); - QString vDesignator(inner.at(i++).toString()); - Q_UNUSED(vDesignator); - QString telephony(inner.at(i++).toString()); - QString name(inner.at(i++).toString()); - QString countryIso(inner.at(i++).toString()); - QString country(inner.at(i++).toString()); - bool va = inner.at(i++).toString().startsWith("Y", Qt::CaseInsensitive); // VA + void CAirlineIcaoCode::updateMissingParts(const CAirlineIcaoCode &otherIcaoCode) + { + if (!this->hasValidDesignator()) { this->setDesignator(otherIcaoCode.getDesignator()); } + if (!this->hasValidCountry()) { this->setCountry(otherIcaoCode.getCountry()); } + if (!this->hasName()) { this->setName(otherIcaoCode.getName()); } + if (!this->hasTelephonyDesignator()) { this->setTelephonyDesignator(otherIcaoCode.getTelephonyDesignator()); } + if (!this->hasValidDbKey()) + { + this->setDbKey(otherIcaoCode.getDbKey()); + this->setUtcTimestamp(otherIcaoCode.getUtcTimestamp()); + } + } + + CAirlineIcaoCode CAirlineIcaoCode::fromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + if (!existsKey(json, prefix)) + { + // when using relationship, this can be null + return CAirlineIcaoCode(); + } + + QString designator(json.value(prefix + "designator").toString()); + QString telephony(json.value(prefix + "callsign").toString()); + QString name(json.value(prefix + "name").toString()); + QString countryIso(json.value(prefix + "country").toString()); + QString countryName(json.value(prefix + "countryname").toString()); + bool va = json.value(prefix + "va").toString().startsWith("Y", Qt::CaseInsensitive); // VA + bool operating = json.value(prefix + "operating").toString().startsWith("Y", Qt::CaseInsensitive); // operating CAirlineIcaoCode code( - designator, name, countryIso, country, telephony, va + designator, name, + CCountry(countryIso, countryName), + telephony, va, operating ); - code.setDbKey(dbKey); + code.setKeyAndTimestampFromDatabaseJson(json, prefix); return code; } diff --git a/src/blackmisc/aviation/airlineicaocode.h b/src/blackmisc/aviation/airlineicaocode.h index 8f21cdf63..b56abbde5 100644 --- a/src/blackmisc/aviation/airlineicaocode.h +++ b/src/blackmisc/aviation/airlineicaocode.h @@ -13,9 +13,11 @@ #define BLACKMISC_AVIATION_AIRLINEICAOCODE_H #include "blackmisc/blackmiscexport.h" +#include "blackmisc/country.h" #include "blackmisc/datastore.h" #include "blackmisc/valueobject.h" #include "blackmisc/propertyindex.h" +#include "blackmisc/statusmessagelist.h" #include "blackmisc/blackmiscfreefunctions.h" namespace BlackMisc @@ -36,7 +38,9 @@ namespace BlackMisc IndexAirlineCountryIso, IndexAirlineCountry, IndexTelephonyDesignator, - IndexIsVirtualAirline + IndexIsVirtualAirline, + IndexIsOperating, + IndexDesignatorNameCountry, }; //! Default constructor. @@ -46,7 +50,7 @@ namespace BlackMisc CAirlineIcaoCode(const QString &airlineDesignator); //! Constructor. - CAirlineIcaoCode(const QString &airlineDesignator, const QString &airlineName, const QString &countryIso, const QString &country, const QString &telephony, bool virtualAirline); + CAirlineIcaoCode(const QString &airlineDesignator, const QString &airlineName, const BlackMisc::CCountry &country, const QString &telephony, bool virtualAirline, bool operating); //! Get airline, e.g. "DLH" const QString &getDesignator() const { return this->m_designator; } @@ -58,16 +62,16 @@ namespace BlackMisc void setDesignator(const QString &icaoDesignator) { this->m_designator = icaoDesignator.trimmed().toUpper(); } //! Get country, e.g. "FR" - const QString &getCountryIso() const { return this->m_countryIso; } + const QString &getCountryIso() const { return this->m_country.getIsoCode(); } //! Get country, e.g. "FRANCE" - const QString &getCountry() const { return this->m_country; } + const BlackMisc::CCountry &getCountry() const { return this->m_country; } - //! Set country ISO - void setCountryIso(const QString &country) { this->m_countryIso = country.trimmed().toUpper(); } + //! Combined string designator, name, country + QString getDesignatorNameCountry() const; //! Set country - void setCountry(const QString &country) { this->m_country = country.trimmed(); } + void setCountry(const BlackMisc::CCountry &country) { this->m_country = country; } //! Get name, e.g. "Lufthansa" const QString &getName() const { return this->m_name; } @@ -87,11 +91,23 @@ namespace BlackMisc //! Virtual airline void setVirtualAirline(bool va) { m_isVa = va; } - //! Country? - bool hasCountryIso() const { return !this->m_countryIso.isEmpty(); } + //! Operating? + bool isOperating() const { return m_isOperating; } - //! Airline available? - bool hasDesignator() const { return !this->m_designator.isEmpty(); } + //! Operating airline? + void setOperating(bool operating) { m_isOperating = operating; } + + //! Country? + bool hasValidCountry() const; + + //! Airline designator available? + bool hasValidDesignator() const; + + //! Matches designator string? + bool matchesDesignator(const QString &designator) const; + + //! Matches v-designator string? + bool matchesVDesignator(const QString &designator) const; //! Telephony designator? bool hasTelephonyDesignator() const { return !this->m_telephonyDesignator.isEmpty(); } @@ -102,6 +118,12 @@ namespace BlackMisc //! Complete data bool hasCompleteData() const; + //! Comined string with key + QString getCombinedStringWithKey() const; + + //! \copydoc CValueObject::toIcon + CIcon toIcon() const; + //! \copydoc CValueObject::convertToQString QString convertToQString(bool i18n = false) const; @@ -111,20 +133,26 @@ namespace BlackMisc //! \copydoc CValueObject::setPropertyByIndex void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); + //! Validate data + BlackMisc::CStatusMessageList validate() const; + + //! Update missing parts + void updateMissingParts(const CAirlineIcaoCode &otherIcaoCode); + //! Valid designator? static bool isValidAirlineDesignator(const QString &airline); //! From our DB JSON - static CAirlineIcaoCode fromDatabaseJson(const QJsonObject &json); + static CAirlineIcaoCode fromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); private: BLACK_ENABLE_TUPLE_CONVERSION(CAirlineIcaoCode) QString m_designator; //!< "DLH" QString m_name; //!< "Lufthansa" - QString m_countryIso; //!< "FR" - QString m_country; //!< clear text, "Germany" + BlackMisc::CCountry m_country; //!< Country QString m_telephonyDesignator; //!< "Speedbird" - bool m_isVa = false; + bool m_isVa = false; //!< virtual airline + bool m_isOperating = true; //!< still operating? }; } // namespace } // namespace @@ -132,12 +160,13 @@ namespace BlackMisc Q_DECLARE_METATYPE(BlackMisc::Aviation::CAirlineIcaoCode) BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Aviation::CAirlineIcaoCode, ( o.m_dbKey, + o.m_timestampMSecsSinceEpoch, o.m_designator, o.m_name, - o.m_countryIso, o.m_country, o.m_telephonyDesignator, - o.m_isVa + o.m_isVa, + o.m_isOperating )) #endif // guard diff --git a/src/blackmisc/aviation/airlineicaocodelist.cpp b/src/blackmisc/aviation/airlineicaocodelist.cpp index 2250b27c8..ee89e1888 100644 --- a/src/blackmisc/aviation/airlineicaocodelist.cpp +++ b/src/blackmisc/aviation/airlineicaocodelist.cpp @@ -17,6 +17,24 @@ namespace BlackMisc CSequence(other) { } + CAirlineIcaoCodeList CAirlineIcaoCodeList::findByDesignator(const QString &designator) + { + if (CAirlineIcaoCode::isValidAirlineDesignator(designator)) { return CAirlineIcaoCodeList(); } + return this->findBy([&](const CAirlineIcaoCode & code) + { + return code.matchesDesignator(designator); + }); + } + + CAirlineIcaoCode CAirlineIcaoCodeList::findByVDesignator(const QString &designator) + { + if (CAirlineIcaoCode::isValidAirlineDesignator(designator)) { return CAirlineIcaoCode(); } + return this->findFirstBy([&](const CAirlineIcaoCode & code) + { + return code.matchesVDesignator(designator); + }); + } + CAirlineIcaoCodeList CAirlineIcaoCodeList::fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete) { CAirlineIcaoCodeList codes; @@ -28,5 +46,17 @@ namespace BlackMisc } return codes; } + + QStringList CAirlineIcaoCodeList::toCompleterStrings() const + { + QStringList c; + for (const CAirlineIcaoCode &icao : *this) + { + QString cs(icao.getCombinedStringWithKey()); + c.append(cs); + } + return c; + } + } // namespace } // namespace diff --git a/src/blackmisc/aviation/airlineicaocodelist.h b/src/blackmisc/aviation/airlineicaocodelist.h index 60d436c53..f3b21d190 100644 --- a/src/blackmisc/aviation/airlineicaocodelist.h +++ b/src/blackmisc/aviation/airlineicaocodelist.h @@ -14,6 +14,7 @@ #include "airlineicaocode.h" #include "blackmisc/blackmiscexport.h" +#include "blackmisc/datastoreobjectlist.h" #include "blackmisc/collection.h" #include "blackmisc/sequence.h" #include @@ -25,6 +26,8 @@ namespace BlackMisc //! Value object encapsulating a list of ICAO codes. class BLACKMISC_EXPORT CAirlineIcaoCodeList : public CSequence, + public BlackMisc::IDatastoreObjectListWithIntegerKey, + public BlackMisc::Mixin::MetaType { public: @@ -36,6 +39,16 @@ namespace BlackMisc //! Construct from a base class object. CAirlineIcaoCodeList(const CSequence &other); + //! Find by designator + //! Not unique because of virtual airlines + CAirlineIcaoCodeList findByDesignator(const QString &designator); + + //! Find by v-designator, this should be unique + CAirlineIcaoCode findByVDesignator(const QString &designator); + + //! String list for completion + QStringList toCompleterStrings() const; + //! From our DB JSON static CAirlineIcaoCodeList fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete = true); }; diff --git a/src/blackmisc/aviation/airporticaocode.h b/src/blackmisc/aviation/airporticaocode.h index b67243e8d..3c17567fa 100644 --- a/src/blackmisc/aviation/airporticaocode.h +++ b/src/blackmisc/aviation/airporticaocode.h @@ -62,8 +62,8 @@ namespace BlackMisc } // namespace BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Aviation::CAirportIcaoCode, ( - attr(o.m_icaoCode, flags()) -)) + attr(o.m_icaoCode, flags()) + )) Q_DECLARE_METATYPE(BlackMisc::Aviation::CAirportIcaoCode) #endif // guard diff --git a/src/blackmisc/aviation/atcstation.h b/src/blackmisc/aviation/atcstation.h index 131bca3a7..5d3cf183e 100644 --- a/src/blackmisc/aviation/atcstation.h +++ b/src/blackmisc/aviation/atcstation.h @@ -241,17 +241,17 @@ namespace BlackMisc private: BLACK_ENABLE_TUPLE_CONVERSION(CAtcStation) - CCallsign m_callsign; - BlackMisc::Network::CUser m_controller; + CCallsign m_callsign; + BlackMisc::Network::CUser m_controller; BlackMisc::PhysicalQuantities::CFrequency m_frequency; - BlackMisc::Geo::CCoordinateGeodetic m_position; - BlackMisc::PhysicalQuantities::CLength m_range; - bool m_isOnline = false; - QDateTime m_bookedFromUtc; - QDateTime m_bookedUntilUtc; + BlackMisc::Geo::CCoordinateGeodetic m_position; + BlackMisc::PhysicalQuantities::CLength m_range; + bool m_isOnline = false; + QDateTime m_bookedFromUtc; + QDateTime m_bookedUntilUtc; CInformationMessage m_atis { CInformationMessage::ATIS }; CInformationMessage m_metar { CInformationMessage::METAR }; - BlackMisc::Audio::CVoiceRoom m_voiceRoom; + BlackMisc::Audio::CVoiceRoom m_voiceRoom; }; } // namespace } // namespace diff --git a/src/blackmisc/aviation/aviation.h b/src/blackmisc/aviation/aviation.h index 564b2e502..939d95110 100644 --- a/src/blackmisc/aviation/aviation.h +++ b/src/blackmisc/aviation/aviation.h @@ -28,7 +28,6 @@ #include "blackmisc/aviation/atcstationlist.h" #include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/aviation/aircrafticaocodelist.h" -#include "blackmisc/aviation/aircraftlist.h" #include "blackmisc/aviation/aircraftsituation.h" #include "blackmisc/aviation/aircraftsituationlist.h" #include "blackmisc/aviation/airlineicaocode.h" diff --git a/src/blackmisc/aviation/livery.cpp b/src/blackmisc/aviation/livery.cpp index 17b65d871..495c1ec7c 100644 --- a/src/blackmisc/aviation/livery.cpp +++ b/src/blackmisc/aviation/livery.cpp @@ -25,24 +25,65 @@ namespace BlackMisc CLivery::CLivery() { } + CLivery::CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description) : + CLivery(-1, combinedCode, airline, description, "", "", false) + { } + CLivery::CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const QString &colorFuselage, const QString &colorTail, bool isMilitary) : CLivery(-1, combinedCode, airline, description, colorFuselage, colorTail, isMilitary) { } + CLivery::CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const CRgbColor &colorFuselage, const CRgbColor &colorTail, bool isMilitary) : + CLivery(-1, combinedCode, airline, description, colorFuselage, colorTail, isMilitary) + { } + CLivery::CLivery(int dbKey, const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const QString &colorFuselage, const QString &colorTail, bool isMilitary) : - m_dbKey(dbKey), m_airline(airline), + IDatastoreObjectWithIntegerKey(dbKey), + m_airline(airline), m_combinedCode(combinedCode.trimmed().toUpper()), m_description(description.trimmed()), - m_colorFuselage(normalizeHexColor(colorFuselage)), m_colorTail(normalizeHexColor(colorTail)), + m_colorFuselage(CRgbColor(colorFuselage)), m_colorTail(CRgbColor(colorTail)), m_military(isMilitary) { } + CLivery::CLivery(int dbKey, const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const CRgbColor &colorFuselage, const CRgbColor &colorTail, bool isMilitary) : + IDatastoreObjectWithIntegerKey(dbKey), + m_airline(airline), + m_combinedCode(combinedCode.trimmed().toUpper()), m_description(description.trimmed()), + m_colorFuselage(colorFuselage), m_colorTail(colorTail), + m_military(isMilitary) + { } + + bool CLivery::setAirlineIcaoCode(const CAirlineIcaoCode &airlineIcao) + { + if (m_airline == airlineIcao) { return false; } + m_airline = airlineIcao; + return true; + } + + bool CLivery::hasColorFuselage() const + { + return m_colorFuselage.isValid(); + } + + bool CLivery::hasColorTail() const + { + return m_colorTail.isValid(); + } + + bool CLivery::matchesCombinedCode(const QString &candidate) const + { + if (candidate.isEmpty() || !this->hasCombinedCode()) { return false; } + QString c(candidate.trimmed().toUpper()); + return c == this->m_combinedCode; + } + QString CLivery::convertToQString(bool i18n) const { QString s(i18n ? QCoreApplication::translate("Aviation", "Livery") : "Livery"); s.append(m_combinedCode); if (!this->m_description.isEmpty()) { s.append(' ').append(this->m_description); } - if (!this->m_colorFuselage.isEmpty()) { s.append(" F: ").append(this->m_colorFuselage); } - if (!this->m_colorTail.isEmpty()) { s.append(" T: ").append(this->m_colorTail); } + if (this->m_colorFuselage.isValid()) { s.append(" F: ").append(this->m_colorFuselage.hex()); } + if (this->m_colorTail.isValid()) { s.append(" T: ").append(this->m_colorTail.hex()); } if (this->isMilitary()) { s.append(" Military");} return s; @@ -55,38 +96,81 @@ namespace BlackMisc return !m_description.isEmpty() && !m_combinedCode.isEmpty(); } - CLivery CLivery::fromDatabaseJson(const QJsonObject &json) + CStatusMessageList CLivery::validate() const { - QJsonArray inner = json["cell"].toArray(); - Q_ASSERT_X(!inner.isEmpty(), Q_FUNC_INFO, "Missing JSON"); - if (inner.isEmpty()) { return CLivery(); } + static const CLogCategoryList cats( { CLogCategory(this->getClassName()), CLogCategory::validation()}); + CStatusMessageList msg; + if (!hasCombinedCode()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Livery: missing livery code")); } + if (!hasColorFuselage()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, "Livery: no fuselage color")); } + if (!hasColorTail()) { msg.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, "Livery: no tail color")); } + if (!getAirlineIcaoCodeDesignator().isEmpty()) { msg.push_back(m_airline.validate()); } + return msg; + } - int i = 0; - int dbKey = inner.at(i++).toInt(-1); - QString code(inner.at(i++).toString()); - QString combinedCode(inner.at(i++).toString()); - QString airlineWithId(inner.at(i++).toString()); - QString airlineName(inner.at(i++).toString()); - QString description(inner.at(i++).toString()); - QString colorFuselage(normalizeHexColor(inner.at(i++).toString())); - QString colorTail(normalizeHexColor(inner.at(i++).toString())); - bool military = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); + bool CLivery::hasValidAirlineDesignator() const + { + return m_airline.hasValidDesignator(); + } - int airlineId(CDatastoreUtility::extractIntegerKey(airlineWithId)); - CAirlineIcaoCode airline; - airline.setDbKey(airlineId); - airline.setName(airlineName); - Q_ASSERT_X(code.length() > 0, Q_FUNC_INFO, "Missing code"); + bool CLivery::hasCombinedCode() const + { + Q_ASSERT_X(!m_combinedCode.startsWith("." + standardLiveryMarker()), Q_FUNC_INFO, "illegal combined code"); + return !m_combinedCode.isEmpty(); + } + + bool CLivery::isAirlineLivery() const + { + return (this->m_airline.hasValidDesignator()); + } + + bool CLivery::isAirlineStandardLivery() const + { + if (isColorLivery()) { return false; } + return (this->m_airline.hasValidDesignator() && this->m_combinedCode.endsWith(standardLiveryMarker())); + } + + bool CLivery::isColorLivery() const + { + return m_combinedCode.startsWith(colorLiveryMarker()); + } + + CLivery CLivery::fromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + if (!existsKey(json, prefix)) + { + // when using relationship, this can be null + return CLivery(); + } + + QString combinedCode(json.value(prefix + "combinedcode").toString()); + QString description(json.value(prefix + "description").toString()); + CRgbColor colorFuselage(json.value(prefix + "colorfuselage").toString()); + CRgbColor colorTail(json.value(prefix + "colortail").toString()); + bool military = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "military").toString()); + CAirlineIcaoCode airline(CAirlineIcaoCode::fromDatabaseJson(json, "al_")); Q_ASSERT_X(description.length() > 0, Q_FUNC_INFO, "require description"); - CLivery livery(dbKey, combinedCode, airline, description, colorFuselage, colorTail, military); + CLivery livery(combinedCode, airline, description, colorFuselage, colorTail, military); + livery.setKeyAndTimestampFromDatabaseJson(json, prefix); return livery; } - QString CLivery::normalizeHexColor(const QString &color) + bool CLivery::isValidCombinedCode(const QString &candidate) { - if (color.isEmpty()) { return ""; } - QString c = color.trimmed().replace('#', "").toUpper(); - return c; + if (candidate.isEmpty()) { return false; } + if (candidate.count('.') != 1) { return false; } + return candidate.length() > 2; + } + + const QString &CLivery::standardLiveryMarker() + { + static const QString s("_STD"); + return s; + } + + const QString &CLivery::colorLiveryMarker() + { + static const QString s("_CC"); + return s; } CVariant CLivery::propertyByIndex(const BlackMisc::CPropertyIndex &index) const @@ -98,10 +182,12 @@ namespace BlackMisc { case IndexDescription: return CVariant::fromValue(m_description); + case IndexAirlineIcaoCode: + return m_airline.propertyByIndex(index.copyFrontRemoved()); case IndexColorFuselage: - return CVariant::fromValue(this->m_colorFuselage); + return this->m_colorFuselage.propertyByIndex(index.copyFrontRemoved());; case IndexColorTail: - return CVariant::fromValue(this->m_colorTail); + return this->m_colorTail.propertyByIndex(index.copyFrontRemoved()); case IndexCombinedCode: return CVariant::fromValue(this->m_combinedCode); case IndexIsMilitary: @@ -121,11 +207,14 @@ namespace BlackMisc case IndexDescription: this->m_description = variant.toQString(false); break; + case IndexAirlineIcaoCode: + this->m_airline.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; case IndexColorFuselage: - this->setColorFuselage(variant.toQString(false)); + this->m_colorFuselage.setPropertyByIndex(variant, index.copyFrontRemoved()); break; case IndexColorTail: - this->setColorTail(variant.toQString(false)); + this->m_colorTail.setPropertyByIndex(variant, index.copyFrontRemoved()); break; case IndexCombinedCode: this->setCombinedCode(variant.toQString(false)); @@ -139,5 +228,20 @@ namespace BlackMisc } } + void CLivery::updateMissingParts(const CLivery &otherLivery) + { + if (!this->m_colorFuselage.isValid()) { this->setColorFuselage(otherLivery.getColorFuselage()); } + if (!this->m_colorTail.isValid()) { this->setColorTail(otherLivery.getColorTail()); } + if (this->m_combinedCode.isEmpty()) { this->setCombinedCode(otherLivery.getCombinedCode());} + if (this->m_description.isEmpty()) { this->setDescription(otherLivery.getDescription());} + + this->m_airline.updateMissingParts(otherLivery.getAirlineIcaoCode()); + if (!this->hasValidDbKey()) + { + this->setDbKey(otherLivery.getDbKey()); + this->setUtcTimestamp(otherLivery.getUtcTimestamp()); + } + } + } // namespace } // namespace diff --git a/src/blackmisc/aviation/livery.h b/src/blackmisc/aviation/livery.h index d98363288..bee826f71 100644 --- a/src/blackmisc/aviation/livery.h +++ b/src/blackmisc/aviation/livery.h @@ -15,6 +15,7 @@ #include "blackmisc/blackmiscexport.h" #include "blackmisc/aviation/airlineicaocode.h" #include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/rgbcolor.h" #include "blackmisc/propertyindex.h" #include "blackmisc/datastore.h" @@ -32,6 +33,7 @@ namespace BlackMisc enum ColumnIndex { IndexDescription = BlackMisc::CPropertyIndex::GlobalIndexCLivery, + IndexAirlineIcaoCode, IndexCombinedCode, IndexColorFuselage, IndexColorTail, @@ -41,14 +43,26 @@ namespace BlackMisc //! Default constructor. CLivery(); + //! Constructor + CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description); + //! Constructor CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const QString &colorFuselage, const QString &colorTail, bool isMilitary); + //! Constructor + CLivery(const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const BlackMisc::CRgbColor &colorFuselage, const BlackMisc::CRgbColor &colorTail, bool isMilitary); + //! Constructor CLivery(int dbKey, const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const QString &colorFuselage, const QString &colorTail, bool isMilitary); + //! Constructor + CLivery(int dbKey, const QString &combinedCode, const CAirlineIcaoCode &airline, const QString &description, const BlackMisc::CRgbColor &colorFuselage, const BlackMisc::CRgbColor &colorTail, bool isMilitary); + //! Corresponding airline, if any - const CAirlineIcaoCode &getAirlineIcao() const { return m_airline; } + const CAirlineIcaoCode &getAirlineIcaoCode() const { return m_airline; } + + //! Corresponding airline designator, if any + const QString &getAirlineIcaoCodeDesignator() const { return m_airline.getDesignator(); } //! Combined code const QString &getCombinedCode() const { return m_combinedCode; } @@ -57,29 +71,41 @@ namespace BlackMisc const QString &getDescription() const { return m_description; } //! Get fuselage color. - const QString &getColorFuselage() const { return m_colorFuselage; } + const BlackMisc::CRgbColor &getColorFuselage() const { return m_colorFuselage; } //! Get tail color. - const QString &getColorTails() const { return m_colorTail; } + const BlackMisc::CRgbColor &getColorTail() const { return m_colorTail; } //! Military livery bool isMilitary() const { return m_military; } //! Airline ICAO code - void setAirlineIcao(const CAirlineIcaoCode &airlineIcao) { m_airline = airlineIcao; } + bool setAirlineIcaoCode(const CAirlineIcaoCode &airlineIcao); //! Combined code void setCombinedCode(const QString &code) { m_combinedCode = code.trimmed().toUpper(); } //! Set fuselage color - void setColorFuselage(const QString &color) { this->m_colorFuselage = normalizeHexColor(color); } + void setColorFuselage(const BlackMisc::CRgbColor &color) { this->m_colorFuselage = color; } //! Set tail color - void setColorTail(const QString &color) { this->m_colorTail = normalizeHexColor(color); } + void setColorTail(const BlackMisc::CRgbColor &color) { this->m_colorTail = color; } + + //! Fuselage color set? + bool hasColorFuselage() const; + + //! Tail color set? + bool hasColorTail() const; + + //! Set description + void setDescription(const QString &description) { this->m_description = description; } //! Military aircraft? void setMilitary(bool isMilitary) { this->m_military = isMilitary; } + //! Matches combined code + bool matchesCombinedCode(const QString &candidate) const; + //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; @@ -92,31 +118,59 @@ namespace BlackMisc //! Complete data? bool hasCompleteData() const; + //! Validate data + BlackMisc::CStatusMessageList validate() const; + + //! Airline available? + bool hasValidAirlineDesignator() const; + + //! Livery combined code available? + bool hasCombinedCode() const; + + //! Livery representing airline + bool isAirlineLivery() const; + + //! Livery representing airline standard livery + bool isAirlineStandardLivery() const; + + //! Color livery + bool isColorLivery() const; + + //! Update missing parts + void updateMissingParts(const CLivery &otherLivery); + //! Object from JSON - static CLivery fromDatabaseJson(const QJsonObject &json); + static CLivery fromDatabaseJson(const QJsonObject &json, const QString &prefix = QString("liv_")); + + //! Valid combined code string? + static bool isValidCombinedCode(const QString &candidate); + + //! Standard livery marker + static const QString &standardLiveryMarker(); + + //! Color livery marker + static const QString &colorLiveryMarker(); private: BLACK_ENABLE_TUPLE_CONVERSION(CLivery) - int m_dbKey = -1; //!< optional DB key CAirlineIcaoCode m_airline; //!< corresponding airline, if any QString m_combinedCode; //!< livery code and pseudo airline ICAO code - QString m_description; - QString m_colorFuselage; - QString m_colorTail; + QString m_description; //!< describes the livery + BlackMisc::CRgbColor m_colorFuselage; //! color of fuselage + BlackMisc::CRgbColor m_colorTail; //! color of tail bool m_military = false; //! Military livery? - - static QString normalizeHexColor(const QString &color); }; } // namespace } // namespace BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Aviation::CLivery, ( attr(o.m_dbKey), + attr(o.m_timestampMSecsSinceEpoch), attr(o.m_airline), attr(o.m_combinedCode, flags ()), attr(o.m_description), - attr(o.m_colorFuselage, flags ()), - attr(o.m_colorTail, flags ()) + attr(o.m_colorFuselage), + attr(o.m_colorTail) )) Q_DECLARE_METATYPE(BlackMisc::Aviation::CLivery) diff --git a/src/blackmisc/aviation/liverylist.cpp b/src/blackmisc/aviation/liverylist.cpp index 1dd897075..e587ea8a7 100644 --- a/src/blackmisc/aviation/liverylist.cpp +++ b/src/blackmisc/aviation/liverylist.cpp @@ -23,11 +23,32 @@ namespace BlackMisc CSequence(other) { } - CLiveryList CLiveryList::findByCombinedCode(const QString &combinedCode) const + CLiveryList CLiveryList::findByAirlineIcaoDesignator(const QString &icao) const { - QString cc(combinedCode.trimmed().toUpper()); - if (cc.isEmpty()) { return CLiveryList();} - return this->findBy(&CLivery::getCombinedCode, cc); + QString i(icao.trimmed().toUpper()); + if (i.isEmpty()) { return CLiveryList(); } + return this->findBy(&CLivery::getAirlineIcaoCodeDesignator, i); + } + + CLivery CLiveryList::findByAirlineIcaoDesignatorStdLivery(const QString &icao) const + { + QString i(icao.trimmed().toUpper()); + if (i.isEmpty()) { return CLivery(); } + return this->findFirstByOrDefault([&](const CLivery & livery) + { + return livery.getAirlineIcaoCodeDesignator() == icao && + livery.isAirlineStandardLivery(); + + }); + } + + CLivery CLiveryList::findByCombinedCode(const QString &combinedCode) const + { + if (!CLivery::isValidCombinedCode(combinedCode)) { return CLivery(); } + return this->findFirstByOrDefault([&](const CLivery & livery) + { + return livery.matchesCombinedCode(combinedCode); + }); } } // namespace diff --git a/src/blackmisc/aviation/liverylist.h b/src/blackmisc/aviation/liverylist.h index 82e95a131..58572d1b4 100644 --- a/src/blackmisc/aviation/liverylist.h +++ b/src/blackmisc/aviation/liverylist.h @@ -36,9 +36,14 @@ namespace BlackMisc //! Construct from a base class object. CLiveryList(const CSequence &other); - //! Find 0..n liveries by combined code - CLiveryList findByCombinedCode(const QString &combinedCode) const; + //! Find livery by airline + CLiveryList findByAirlineIcaoDesignator(const QString &icao) const; + //! Find livery by airline + CLivery findByAirlineIcaoDesignatorStdLivery(const QString &icao) const; + + //! Find livery by combined code + CLivery findByCombinedCode(const QString &combinedCode) const; }; } //namespace } // namespace diff --git a/src/blackmisc/country.cpp b/src/blackmisc/country.cpp new file mode 100644 index 000000000..88271dbee --- /dev/null +++ b/src/blackmisc/country.cpp @@ -0,0 +1,151 @@ +/* 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 and at http://www.swift-project.org/license.html. 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/country.h" +#include + +namespace BlackMisc +{ + + CCountry::CCountry(const QString &iso, const QString &name) : + IDatastoreObjectWithStringKey(iso.trimmed().toUpper()), + m_name(name.trimmed()) + { } + + CIcon CCountry::toIcon() const + { + if (this->m_dbKey.length() == 2) + { + return CIcon("images/flags/" + m_dbKey.toLower() + ".png", + this->convertToQString()); + } + else + { + return CIcon::iconByIndex(CIcons::StandardIconEmpty); + } + } + + void CCountry::setIsoCode(const QString &iso) + { + m_dbKey = iso.trimmed().toUpper(); + Q_ASSERT_X(m_dbKey.length() == 2, Q_FUNC_INFO, "wromg ISO code"); + } + + bool CCountry::hasIsoCode() const + { + return m_dbKey.length() == 2; + } + + QString CCountry::getCombinedStringIsoName() const + { + if (!this->hasIsoCode()) { return QString(); } + QString s(m_dbKey); + if (this->m_name.isEmpty()) { return s; } + s.append(" (").append(m_name).append(")"); + return s; + } + + QString CCountry::getCombinedStringNameIso() const + { + if (!this->isValid()) { return QString(); } + QString s(m_name); + s.append(" - ").append(m_dbKey); + return s; + } + + void CCountry::setName(const QString &countryName) + { + m_name = countryName.trimmed(); + } + + bool CCountry::matchesCountryName(const QString &name) const + { + if (name.isEmpty() || m_name.isEmpty()) { return false; } + if (name.length() < 5) + { + return m_name.length() == name.length() && m_name.startsWith(name, Qt::CaseInsensitive); + } + else + { + return m_name.contains(name, Qt::CaseInsensitive); + } + } + + bool CCountry::isValid() const + { + return m_dbKey.length() == 2 && !m_name.isEmpty(); + } + + QString CCountry::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + return this->getCombinedStringIsoName(); + } + + CVariant CCountry::propertyByIndex(const BlackMisc::CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexIsoCode: + return CVariant::fromValue(m_dbKey); + case IndexName: + return CVariant::fromValue(m_name); + case IndexIsoName: + return CVariant::fromValue(getCombinedStringIsoName()); + case IndexNameIso: + return CVariant::fromValue(getCombinedStringNameIso()); + default: + return (IDatastoreObjectWithStringKey::canHandleIndex(index)) ? + IDatastoreObjectWithStringKey::propertyByIndex(index) : + CValueObject::propertyByIndex(index); + } + } + + void CCountry::setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexIsoCode: + this->setIsoCode(variant.toQString()); + break; + case IndexName: + this->setName(variant.toQString()); + break; + default: + return (IDatastoreObjectWithStringKey::canHandleIndex(index)) ? + IDatastoreObjectWithStringKey::setPropertyByIndex(variant, index) : + CValueObject::setPropertyByIndex(variant, index); + break; + } + } + + CCountry CCountry::fromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + if (!existsKey(json, prefix)) + { + // when using relationship, this can be null + return CCountry(); + } + QString iso(json.value(prefix + "id").toString()); + QString name(json.value(prefix + "country").toString()); + CCountry country(iso, name); + country.setKeyAndTimestampFromDatabaseJson(json, prefix); + return country; + } + + bool CCountry::isValidIsoCode(const QString &isoCode) + { + return isoCode.length() == 2; + } + +} // namespace diff --git a/src/blackmisc/country.h b/src/blackmisc/country.h new file mode 100644 index 000000000..356983745 --- /dev/null +++ b/src/blackmisc/country.h @@ -0,0 +1,104 @@ +/* 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 and at http://www.swift-project.org/license.html. 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. + */ + +//! \file + +#ifndef BLACKMISC_COUNTRY_H +#define BLACKMISC_COUNTRY_H + +#include "blackmiscexport.h" +#include "datastore.h" +#include "valueobject.h" +#include + +namespace BlackMisc +{ + /*! + * Color + */ + class BLACKMISC_EXPORT CCountry : + public CValueObject, + public IDatastoreObjectWithStringKey + { + public: + //! Properties by index + enum ColumnIndex + { + IndexIsoCode = BlackMisc::CPropertyIndex::GlobalIndexCountry, + IndexName, + IndexNameIso, + IndexIsoName + }; + + //! Constructor + CCountry() = default; + + //! Constructor + CCountry(const QString &iso, const QString &name); + + //! Valid? + bool isValid() const; + + //! Representing icon + CIcon toIcon() const; + + //! DB ISO code + const QString &getIsoCode() const { return m_dbKey; } + + //! Country ISO code (US, DE, GB, PL) + void setIsoCode(const QString &iso); + + //! ISO code? + bool hasIsoCode() const; + + //! Country name + const QString &getName() const { return m_name; } + + //! Combined string ISO/name + QString getCombinedStringIsoName() const; + + //! Combined string name/ISO + QString getCombinedStringNameIso() const; + + //! Set country name + void setName(const QString &countryName); + + //! Matches country name + bool matchesCountryName(const QString &name) const; + + //! \copydoc CValueObject::convertToQString + QString convertToQString(bool i18n = false) const; + + //! \copydoc CValueObject::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc CValueObject::setPropertyByIndex + void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); + + //! From our database JSON format + static CCountry fromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); + + //! Valid country iso code + static bool isValidIsoCode(const QString &isoCode); + + private: + BLACK_ENABLE_TUPLE_CONVERSION(CCountry) + QString m_name; //!< country name + + }; +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::CCountry) +BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CCountry, ( + attr(o.m_dbKey), + attr(o.m_timestampMSecsSinceEpoch), + attr(o.m_name) + )) + +#endif // guard diff --git a/src/blackmisc/countrylist.cpp b/src/blackmisc/countrylist.cpp new file mode 100644 index 000000000..b387ca19e --- /dev/null +++ b/src/blackmisc/countrylist.cpp @@ -0,0 +1,91 @@ +/* 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 and at http://www.swift-project.org/license.html. 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 "countrylist.h" + +namespace BlackMisc +{ + CCountryList::CCountryList(const CSequence &other) : + CSequence(other) + { } + + CCountry CCountryList::findByIsoCode(const QString &isoCode) const + { + QString iso(isoCode.trimmed().toUpper()); + if (!CCountry::isValidIsoCode(iso)) { return CCountry(); } + return IDatastoreObjectListWithStringKey::findByKey(isoCode); + } + + CCountry CCountryList::findBestMatchByCountryName(const QString &countryName) const + { + if (countryName.isEmpty()) { return CCountry(); } + CCountryList countries = this->findBy([&](const CCountry & country) + { + return country.matchesCountryName(countryName); + }); + if (countries.size() < 2) { return countries.frontOrDefault(); } + + // find best match + for (const CCountry &c : countries) + { + if (c.getName() == countryName) { return c; } + if (c.getName().startsWith(countryName, Qt::CaseInsensitive)) { return c; } + } + return countries.front(); + } + + QStringList CCountryList::toIsoNameList() const + { + QStringList sl; + for (const CCountry &country : (*this)) + { + QString s = country.getCombinedStringIsoName(); + if (s.isEmpty()) { continue; } + sl.append(s); + } + return sl; + } + + QStringList CCountryList::toNameIsoList() const + { + QStringList sl; + for (const CCountry &country : (*this)) + { + QString s = country.getCombinedStringNameIso(); + if (s.isEmpty()) { continue; } + sl.append(s); + } + return sl; + } + + QStringList CCountryList::toNameList() const + { + QStringList sl; + for (const CCountry &country : (*this)) + { + QString s = country.getName(); + if (s.isEmpty()) { continue; } + sl.append(s); + } + return sl; + } + + + CCountryList CCountryList::fromDatabaseJson(const QJsonArray &array) + { + CCountryList countries; + for (const QJsonValue &value : array) + { + CCountry country(CCountry::fromDatabaseJson(value.toObject())); + countries.push_back(country); + } + return countries; + } + +} // namespace diff --git a/src/blackmisc/countrylist.h b/src/blackmisc/countrylist.h new file mode 100644 index 000000000..089a1700a --- /dev/null +++ b/src/blackmisc/countrylist.h @@ -0,0 +1,65 @@ +/* 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 and at http://www.swift-project.org/license.html. 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. + */ + +//! \file + +#ifndef BLACKMISC_COUNTRYLIST_H +#define BLACKMISC_COUNTRYLIST_H + +#include "country.h" +#include "blackmisc/blackmiscexport.h" +#include "datastoreobjectlist.h" +#include "blackmisc/collection.h" +#include "blackmisc/sequence.h" +#include + +namespace BlackMisc +{ + //! Value object encapsulating a list of countries. + //! \remark: I do not use CCollection as I want to sort per column + class BLACKMISC_EXPORT CCountryList : + public CSequence, + public BlackMisc::IDatastoreObjectListWithStringKey, + public BlackMisc::Mixin::MetaType + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CCountryList) + + //! Default constructor. + CCountryList() = default; + + //! Construct from a base class object. + CCountryList(const CSequence &other); + + //! Find by ISO code + CCountry findByIsoCode(const QString &isoCode) const; + + //! Find "best match" by country + CCountry findBestMatchByCountryName(const QString &countryName) const; + + //! ISO/name string list + QStringList toIsoNameList() const; + + //! Name/ISO string list + QStringList toNameIsoList() const; + + //! Name string list + QStringList toNameList() const; + + //! From our database JSON format + static CCountryList fromDatabaseJson(const QJsonArray &array); + }; + +} //namespace + +Q_DECLARE_METATYPE(BlackMisc::CCountryList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/datastore.cpp b/src/blackmisc/datastore.cpp index b94941da7..0351dbc6a 100644 --- a/src/blackmisc/datastore.cpp +++ b/src/blackmisc/datastore.cpp @@ -8,18 +8,53 @@ */ #include "blackmisc/datastore.h" +#include "blackmisc/datastoreutility.h" #include "blackmisc/blackmiscfreefunctions.h" namespace BlackMisc { + QString IDatastoreObjectWithIntegerKey::getDbKeyAsString() const + { + if (this->m_dbKey < 0) { return ""; } + return QString::number(this->m_dbKey); + } + + QString IDatastoreObjectWithIntegerKey::getDbKeyAsStringInParentheses() const + { + if (this->m_dbKey < 0) { return ""; } + return "(" + QString::number(this->m_dbKey) + ")"; + } + + void IDatastoreObjectWithIntegerKey::setDbKey(const QString &key) + { + bool ok; + int k = key.toInt(&ok); + if (!ok) { k = -1; } + this->m_dbKey = k; + } + + void IDatastoreObjectWithIntegerKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + int dbKey = json.value(prefix + "id").toInt(-1); + QString timestampString(json.value(prefix + "lastupdated").toString()); + QDateTime ts(CDatastoreUtility::parseTimestamp(timestampString)); + this->setDbKey(dbKey); + this->setUtcTimestamp(ts); + } + + bool IDatastoreObjectWithIntegerKey::existsKey(const QJsonObject &json, const QString &prefix) + { + return !json.value(prefix + "id").isNull(); + } + CVariant IDatastoreObjectWithIntegerKey::propertyByIndex(const CPropertyIndex &index) const { if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } ColumnIndex i = index.frontCasted(); switch (i) { - case IndexDbIntergerKey: return CVariant::from(this->m_dbKey); + case IndexDbIntegerKey: return CVariant::from(this->m_dbKey); default: break; } @@ -37,7 +72,7 @@ namespace BlackMisc ColumnIndex i = index.frontCasted(); switch (i) { - case IndexDbIntergerKey: + case IndexDbIntegerKey: this->m_dbKey = variant.toInt(); break; default: @@ -49,7 +84,20 @@ namespace BlackMisc { if (ITimestampBased::canHandleIndex(index)) { return true;} int i = index.frontCasted(); - return (i >= static_cast(IndexDbIntergerKey)) && (i <= static_cast(IndexDbIntergerKey)); + return (i >= static_cast(IndexDbIntegerKey)) && (i <= static_cast(IndexDbIntegerKey)); + } + + void IDatastoreObjectWithStringKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + QString dbKey = json.value(prefix + "id").toString(); + QDateTime ts(CDatastoreUtility::parseTimestamp(json.value(prefix + "lastupdated").toString())); + this->setDbKey(dbKey); + this->setUtcTimestamp(ts); + } + + bool IDatastoreObjectWithStringKey::existsKey(const QJsonObject &json, const QString &prefix) + { + return !json.value(prefix + "id").isNull(); } CVariant IDatastoreObjectWithStringKey::propertyByIndex(const CPropertyIndex &index) const diff --git a/src/blackmisc/datastore.h b/src/blackmisc/datastore.h index befa83b92..5c75b1a25 100644 --- a/src/blackmisc/datastore.h +++ b/src/blackmisc/datastore.h @@ -28,19 +28,40 @@ namespace BlackMisc //! Property index enum ColumnIndex { - IndexDbIntergerKey = CPropertyIndex::GlobalIndexIDatastoreInteger + IndexDbIntegerKey = CPropertyIndex::GlobalIndexIDatastoreInteger }; //! Get DB key. int getDbKey() const { return m_dbKey; } + //! DB key as string + QString getDbKeyAsString() const; + + //! Db ley in parentheses, e.g. "(3)" + QString getDbKeyAsStringInParentheses() const; + //! Set the DB key void setDbKey(int key) { m_dbKey = key; } + //! DB key passed as string + void setDbKey(const QString &key); + //! Has valid DB key bool hasValidDbKey() const { return m_dbKey >= 0; } protected: + //! Constructor + IDatastoreObjectWithIntegerKey() {} + + //! Constructor + IDatastoreObjectWithIntegerKey(int key) : m_dbKey(key) {} + + //! Set key and timestamp values + void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); + + //! Is a key available? + static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); + //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; @@ -75,6 +96,18 @@ namespace BlackMisc bool hasValidDbKey() const { return !m_dbKey.isEmpty(); } protected: + //! Constructor + IDatastoreObjectWithStringKey() {} + + //! Constructor + IDatastoreObjectWithStringKey(const QString &key) : m_dbKey(key) {} + + //! Set key and timestamp values + void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); + + //! Is a key available? + static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); + //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; diff --git a/src/blackmisc/datastoreobjectlist.cpp b/src/blackmisc/datastoreobjectlist.cpp index f1e743537..d114f5184 100644 --- a/src/blackmisc/datastoreobjectlist.cpp +++ b/src/blackmisc/datastoreobjectlist.cpp @@ -9,7 +9,10 @@ #include "blackmisc/datastoreobjectlist.h" #include "blackmisc/predicates.h" +#include "blackmisc/countrylist.h" #include "blackmisc/aviation/liverylist.h" +#include "blackmisc/aviation/aircrafticaocodelist.h" +#include "blackmisc/aviation/airlineicaocodelist.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/distributorlist.h" #include @@ -26,7 +29,7 @@ namespace BlackMisc { } template - OBJ IDatastoreObjectListWithIntegerKey::findByKey(int key, const OBJ ¬Found ) const + OBJ IDatastoreObjectListWithIntegerKey::findByKey(int key, const OBJ ¬Found) const { return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); } @@ -49,6 +52,17 @@ namespace BlackMisc this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getDbKey)); } + template + QStringList IDatastoreObjectListWithStringKey::toDbKeyList() const + { + QStringList keys; + for (const OBJ &obj : ITimestampObjectList::container()) + { + keys.append(obj.getDbKey()); + } + return keys; + } + template CONTAINER IDatastoreObjectListWithIntegerKey::fromDatabaseJson(const QJsonArray &array) { @@ -74,7 +88,10 @@ namespace BlackMisc // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class IDatastoreObjectListWithIntegerKey; + template class IDatastoreObjectListWithIntegerKey; + template class IDatastoreObjectListWithIntegerKey; template class IDatastoreObjectListWithIntegerKey; template class IDatastoreObjectListWithStringKey; + template class IDatastoreObjectListWithStringKey; } // namespace diff --git a/src/blackmisc/datastoreobjectlist.h b/src/blackmisc/datastoreobjectlist.h index 4aa5e2c7f..c6869d87e 100644 --- a/src/blackmisc/datastoreobjectlist.h +++ b/src/blackmisc/datastoreobjectlist.h @@ -50,6 +50,9 @@ namespace BlackMisc //! Sort by timestamp void sortByKey(); + //! All keys as string list + QStringList toDbKeyList() const; + //! From DB JSON static CONTAINER fromDatabaseJson(const QJsonArray &array); diff --git a/src/blackmisc/datastoreutility.cpp b/src/blackmisc/datastoreutility.cpp index 237dde8ca..99d667a64 100644 --- a/src/blackmisc/datastoreutility.cpp +++ b/src/blackmisc/datastoreutility.cpp @@ -9,6 +9,8 @@ #include "blackmisc/datastoreutility.h" #include "blackmisc/blackmiscfreefunctions.h" +#include +#include namespace BlackMisc { @@ -19,13 +21,65 @@ namespace BlackMisc int CDatastoreUtility::extractIntegerKey(const QString &stringWithKey) { - int i1 = stringWithKey.lastIndexOf('('); - if (i1 < 0) { return -1; } - int i2 = stringWithKey.lastIndexOf(')'); - if (i2 <= i1 + 1) { return -1;} - QString n(stringWithKey.mid(i1 + 1, i2 - i1 - 1)); + QString ks(stringWithKey.trimmed()); + if (ks.isEmpty()) { return -1; } bool ok = false; - int key = n.toInt(&ok); + int key = ks.toInt(&ok); + if (ok) { return key; } // only a number + + // key in string with () + int i1 = ks.lastIndexOf('('); + if (i1 < 0) { return -1; } + int i2 = ks.lastIndexOf(')'); + if (i2 <= i1 + 1) { return -1;} + QString n(ks.mid(i1 + 1, i2 - i1 - 1)); + ok = false; + key = n.toInt(&ok); return ok ? key : -1; } + + QDateTime CDatastoreUtility::parseTimestamp(const QString ×tamp) + { + Q_ASSERT_X(!timestamp.isEmpty(), Q_FUNC_INFO, "Missing timestamp"); + if (!timestamp.isEmpty()) + { + QString ts(timestamp.trimmed().remove(' ').remove('-').remove(':')); // normalize + QDateTime dt = QDateTime::fromString(ts, "yyyyMMddHHmmss"); + return dt; + } + else + { + return QDateTime(); + } + } + + bool CDatastoreUtility::parseSwiftWriteResponse(const QString &jsonResponse, CStatusMessageList &messages, CVariant &key) + { + if (jsonResponse.isEmpty()) { return ""; } + QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonResponse.toUtf8())); + if (!jsonDoc.isObject()) { return ""; } + QJsonObject json(jsonDoc.object()); + Q_ASSERT_X(!json.value("id").isNull(), Q_FUNC_INFO, "malformed response"); + if (json.value("id").isNull()) { return false; } + QString id(json.value("id").toString().trimmed()); + QJsonArray msgObject(json.value("messages").toArray()); + messages.push_back(CStatusMessageList::fromDatabaseJson(msgObject)); + bool success = false; + + int intKey; + bool isInt; + intKey = id.toInt(&isInt); + if (isInt) + { + key.setValue(intKey); + success = (intKey >= 0); + } + else + { + key.setValue(id); + success = !id.isEmpty(); + } + return success; + } + } // namespace diff --git a/src/blackmisc/datastoreutility.h b/src/blackmisc/datastoreutility.h index bf6818e51..236b4a66f 100644 --- a/src/blackmisc/datastoreutility.h +++ b/src/blackmisc/datastoreutility.h @@ -12,7 +12,8 @@ #ifndef BLACKMISC_DATASTOREUTILITY_H #define BLACKMISC_DATASTOREUTILITY_H -#include "blackmiscexport.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/statusmessagelist.h" #include "timestampbased.h" #include "propertyindex.h" #include "variant.h" @@ -29,10 +30,16 @@ namespace BlackMisc CDatastoreUtility() = delete; //! DB Bool value to bool - static bool dbBoolStringToBool(const QString &dbBool); + BLACKMISC_EXPORT static bool dbBoolStringToBool(const QString &dbBool); //! Extract key from string like "MyAircraft (33)" - static int extractIntegerKey(const QString &stringWithKey); + BLACKMISC_EXPORT static int extractIntegerKey(const QString &stringWithKey); + + //! Parse a timestamp object + BLACKMISC_EXPORT static QDateTime parseTimestamp(const QString ×tamp); + + //! Get id from a DB response + BLACKMISC_EXPORT static bool parseSwiftWriteResponse(const QString &jsonResponse, BlackMisc::CStatusMessageList &messages, BlackMisc::CVariant &key); }; } // namespace diff --git a/src/blackmisc/network/user.cpp b/src/blackmisc/network/user.cpp index bae41ff7d..8397c77a0 100644 --- a/src/blackmisc/network/user.cpp +++ b/src/blackmisc/network/user.cpp @@ -14,7 +14,7 @@ #include "blackmisc/propertyindex.h" #include "blackmisc/variant.h" #include -#include +#include using namespace BlackMisc::Aviation; @@ -116,45 +116,20 @@ namespace BlackMisc return msgs; } + void CUser::updateMissingParts(const CUser &otherUser) + { + if (this == &otherUser) { return; } + if (!this->hasValidRealName()) { this->setRealName(otherUser.getRealName()); } + if (!this->hasValidId()) { this->setId(otherUser.getId()); } + if (!this->hasValidEmail()) { this->setEmail(otherUser.getEmail()); } + if (!this->hasValidCallsign()) { this->setCallsign(otherUser.getCallsign()); } + } + void CUser::syncronizeData(CUser &otherUser) { - if (otherUser == (*this)) { return; } - - if (this->hasValidRealName()) - { - otherUser.setRealName(this->getRealName()); - } - else if (otherUser.hasValidRealName()) - { - this->setRealName(otherUser.getRealName()); - } - - if (this->hasValidId()) - { - otherUser.setId(this->getId()); - } - else if (otherUser.hasValidId()) - { - this->setId(otherUser.getId()); - } - - if (this->hasValidEmail()) - { - otherUser.setEmail(this->getEmail()); - } - else if (otherUser.hasValidEmail()) - { - this->setEmail(otherUser.getEmail()); - } - - if (this->hasValidCallsign()) - { - otherUser.setCallsign(this->getCallsign()); - } - else if (otherUser.hasValidCallsign()) - { - this->setCallsign(otherUser.getCallsign()); - } + if (this == &otherUser) { return; } + this->updateMissingParts(otherUser); + otherUser.updateMissingParts(*this); } bool CUser::isValidVatsimId(const QString &id) diff --git a/src/blackmisc/network/user.h b/src/blackmisc/network/user.h index 051272820..2c7132eac 100644 --- a/src/blackmisc/network/user.h +++ b/src/blackmisc/network/user.h @@ -124,12 +124,12 @@ namespace BlackMisc //! \copydoc CValueObject::setPropertyByIndex void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); - /*! - * This and another user exchange missing data. - * This user has priority and overrides first. - */ + //! This and another user exchange missing data, This user has priority and overrides first. void syncronizeData(CUser &otherUser); + //! Update missing parts in this object + void updateMissingParts(const CUser &otherUser); + //! Valid VATSIM id static bool isValidVatsimId(const QString &id); diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 8218f41dc..95453cb20 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -51,6 +51,8 @@ namespace BlackMisc GlobalIndexCNameVariantPair = 300, GlobalIndexTimestampBased = 400, GlobalIndexIdentifier = 500, + GlobalIndexRgbColor = 600, + GlobalIndexCountry = 700, GlobalIndexCCallsign = 1000, GlobalIndexCAircraftSituation = 1200, GlobalIndexCAtcStation = 1300, diff --git a/src/blackmisc/rgbcolor.cpp b/src/blackmisc/rgbcolor.cpp new file mode 100644 index 000000000..0acacff41 --- /dev/null +++ b/src/blackmisc/rgbcolor.cpp @@ -0,0 +1,233 @@ +/* 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 and at http://www.swift-project.org/license.html. 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/rgbcolor.h" +#include "blackmiscfreefunctions.h" +#include + +namespace BlackMisc +{ + + CRgbColor::CRgbColor(const QString &color, bool isName) + { + this->setByString(color, isName); + } + + CRgbColor::CRgbColor(int r, int g, int b) : m_r(r), m_g(g), m_b(b) + { } + + CRgbColor::CRgbColor(const QColor &color) + { + this->setQColor(color); + } + + CIcon CRgbColor::toIcon() const + { + if (this->isValid()) + { + QPixmap pixmap(QSize(16, 16)); + QPainter p(&pixmap); + QBrush brush(this->toQColor()); + p.setBackground(brush); + p.setBrush(this->toQColor()); + p.drawRect(0, 0, 16, 16); + CIcon icon(pixmap, hex()); + return icon; + } + else + { + return CIcon::iconByIndex(CIcons::StandardIconError16); + } + } + + QColor CRgbColor::toQColor() const + { + return QColor(red(), green(), blue()); + } + + bool CRgbColor::setQColor(const QColor &color) + { + if (color.isValid()) + { + m_r = color.red(); + m_g = color.green(); + m_b = color.blue(); + return true; + } + else + { + this->setInvalid(); + return false; + } + } + + int CRgbColor::red() const + { + return m_r; + } + + double CRgbColor::normalizedRed() const + { + double c = red() * 1.0; + return c / colorRange(); + } + + QString CRgbColor::redHex(int digits) const + { + return intToHex(m_r, digits); + } + + int CRgbColor::green() const + { + return m_g; + } + + double CRgbColor::normalizedGreen() const + { + double c = green() * 1.0; + return c / colorRange(); + } + + QString CRgbColor::greenHex(int digits) const + { + return intToHex(m_g, digits); + } + + int CRgbColor::blue() const + { + return m_b; + } + + double CRgbColor::normalizedBlue() const + { + double c = blue() * 1.0; + return c / colorRange(); + } + + QString CRgbColor::blueHex(int digits) const + { + return intToHex(m_b, digits); + } + + QString CRgbColor::hex(bool withHash) const + { + if (!isValid()) { return ""; } + QString h(redHex() + greenHex() + blueHex()); + return withHash ? "#" + h : h; + } + + void CRgbColor::setByString(const QString &color, bool isName) + { + if (color.isEmpty()) { return; } + else if (isName) + { + QColor q(color); + m_r = q.red(); + m_g = q.green(); + m_b = q.blue(); + } + else + { + QString c(color.trimmed()); + QColor q(c); + if (setQColor(q)) { return; } + if (c.startsWith("#")) { this->setInvalid(); return; } + q.setNamedColor("#" + c); + this->setQColor(q); + } + } + + bool CRgbColor::isValid() const + { + return m_r >= 0 && m_g >= 0 && m_b >= 0; + } + + double CRgbColor::colorDistance(const CRgbColor &color) const + { + if (!this->isValid() && !color.isValid()) { return 0; } + if (!this->isValid() || !color.isValid()) { return 1; } + if (*this == color) { return 0.0; } // avoid rounding + + // all values 0-1 + double rd = (normalizedRed() - color.normalizedRed()); + double bd = (normalizedBlue() - color.normalizedBlue()); + double gd = (normalizedGreen() - color.normalizedGreen()); + return (rd * rd + bd * bd + gd * gd) / 3.0; + } + + void CRgbColor::setInvalid() + { + m_r = -1; + m_g = -1; + m_b = -1; + } + + QString CRgbColor::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + return hex(); + } + + CVariant CRgbColor::propertyByIndex(const BlackMisc::CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexBlue: + return CVariant::fromValue(blue()); + case IndexRed: + return CVariant::fromValue(red()); + case IndexGreen: + return CVariant::fromValue(green()); + case IndexWebHex: + return CVariant::fromValue(hex()); + default: + return CValueObject::propertyByIndex(index); + } + } + + void CRgbColor::setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexBlue: + this->m_b = variant.toInt(); + break; + case IndexRed: + this->m_r = variant.toInt(); + break; + case IndexGreen: + this->m_g = variant.toInt(); + break; + case IndexWebHex: + this->setByString(variant.toQString()); + break; + default: + CValueObject::setPropertyByIndex(variant, index); + break; + } + } + + double CRgbColor::colorRange() const + { + if (!this->isValid()) { return 255; } + if (m_b < 256 && m_g < 256 && m_r < 256) { return 255; } + if (m_b < 4096 && m_g < 4096 && m_r < 4096) { return 4095; } + return 65535; + } + + QString CRgbColor::intToHex(int h, int digits) + { + return BlackMisc::intToHex(h, digits); + } + +} // namespace diff --git a/src/blackmisc/rgbcolor.h b/src/blackmisc/rgbcolor.h new file mode 100644 index 000000000..0182c77ae --- /dev/null +++ b/src/blackmisc/rgbcolor.h @@ -0,0 +1,129 @@ +/* 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 and at http://www.swift-project.org/license.html. 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. + */ + +//! \file + +#ifndef BLACKMISC_RGBCOLOR_H +#define BLACKMISC_RGBCOLOR_H + +#include "blackmiscexport.h" +#include "valueobject.h" +#include + +namespace BlackMisc +{ + /*! + * Color + */ + class BLACKMISC_EXPORT CRgbColor : public CValueObject + { + public: + //! Properties by index + enum ColumnIndex + { + IndexRed = BlackMisc::CPropertyIndex::GlobalIndexRgbColor, + IndexGreen, + IndexBlue, + IndexWebHex + }; + + //! Constructor + CRgbColor() = default; + + //! Constructor (hex or color name) + CRgbColor(const QString &color, bool isName = false); + + //! Constructor + CRgbColor(int r, int g, int b); + + //! Constructor from QColor + CRgbColor(const QColor &color); + + //! To QColor + QColor toQColor() const; + + //! Set by QColor + bool setQColor(const QColor &color); + + //! Red + int red() const; + + //! Red 0..1 + double normalizedRed() const; + + //! Red as hex + QString redHex(int digits = 2) const; + + //! Green + int green() const; + + //! Green 0..1 + double normalizedGreen() const; + + //! Green as hex + QString greenHex(int digits = 2) const; + + //! Blue + int blue() const; + + //! Blue 0..1 + double normalizedBlue() const; + + //! Blue as hex + QString blueHex(int digits = 2) const; + + //! Hex value + QString hex(bool withHash = false) const; + + //! Hex or color name + void setByString(const QString &color, bool isName = false); + + //! Valid? + bool isValid() const; + + //! Color distance [0..1] + //! http://stackoverflow.com/questions/4754506/color-similarity-distance-in-rgba-color-space/8796867#8796867 + double colorDistance(const CRgbColor &color) const; + + //! Mark as invalid + void setInvalid(); + + //! Representing icon + CIcon toIcon() const; + + //! \copydoc CValueObject::convertToQString + QString convertToQString(bool i18n = false) const; + + //! \copydoc CValueObject::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc CValueObject::setPropertyByIndex + void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); + + private: + BLACK_ENABLE_TUPLE_CONVERSION(CRgbColor) + int m_r = -1; + int m_g = -1; + int m_b = -1; + + //! Color range 255/4095/65535 + double colorRange() const; + + static QString intToHex(int h, int digits = 2); + }; +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::CRgbColor) +BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CRgbColor, ( + attr(o.m_r), + attr(o.m_g), + attr(o.m_b) + )) + +#endif // guard diff --git a/src/blackmisc/simulation/aircraftmodel.cpp b/src/blackmisc/simulation/aircraftmodel.cpp index 614af8523..f839550eb 100644 --- a/src/blackmisc/simulation/aircraftmodel.cpp +++ b/src/blackmisc/simulation/aircraftmodel.cpp @@ -11,6 +11,8 @@ #include "distributor.h" #include "blackmisc/datastoreutility.h" #include +#include +#include using namespace BlackMisc::Aviation; @@ -19,15 +21,15 @@ namespace BlackMisc namespace Simulation { CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type) : - m_modelString(model), m_modelType(type) + m_modelString(model.trimmed().toUpper()), m_modelType(type) {} - CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, const QString &description, const Aviation::CAircraftIcaoData &icao, const Aviation::CLivery &livery) : - m_icao(icao), m_livery(livery), m_modelString(model), m_description(description), m_modelType(type) + CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, const QString &description, const CAircraftIcaoCode &icao, const Aviation::CLivery &livery) : + m_aircraftIcao(icao), m_livery(livery), m_modelString(model.trimmed().toUpper()), m_description(description.trimmed()), m_modelType(type) {} - CAircraftModel::CAircraftModel(const Aviation::CAircraft &aircraft) : - m_callsign(aircraft.getCallsign()), m_icao(aircraft.getIcaoInfo()), m_livery(aircraft.getLivery()) + CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, CSimulatorInfo &simulator, const QString &name, const QString &description, const CAircraftIcaoCode &icao, const CLivery &livery) : + m_aircraftIcao(icao), m_livery(livery), m_simulator(simulator), m_modelString(model.trimmed().toUpper()), m_modelName(name.trimmed()), m_description(description.trimmed()), m_modelType(type) { } QString CAircraftModel::convertToQString(bool i18n) const @@ -36,7 +38,7 @@ namespace BlackMisc if (!s.isEmpty()) { s += ' '; } s += this->getModelTypeAsString(); s += ' '; - s += this->m_icao.toQString(i18n); + s += this->m_aircraftIcao.toQString(i18n); if (!this->m_fileName.isEmpty()) { s += ' '; @@ -48,6 +50,7 @@ namespace BlackMisc CVariant CAircraftModel::propertyByIndex(const BlackMisc::CPropertyIndex &index) const { if (index.isMyself()) { return CVariant::from(*this); } + if (IDatastoreObjectWithIntegerKey::canHandleIndex(index)) { return IDatastoreObjectWithIntegerKey::propertyByIndex(index); } ColumnIndex i = index.frontCasted(); switch (i) { @@ -59,12 +62,24 @@ namespace BlackMisc return CVariant::fromValue(static_cast(this->m_modelType)); case IndexModelTypeAsString: return CVariant(this->getModelTypeAsString()); + case IndexModelMode: + return CVariant::fromValue(static_cast(this->m_modelMode)); + case IndexModelModeAsString: + return CVariant(this->getModelModeAsString()); + case IndexDistributor: + return m_distributor.propertyByIndex(index.copyFrontRemoved()); + case IndexSimulatorInfo: + return m_simulator.propertyByIndex(index.copyFrontRemoved()); + case IndexSimulatorInfoAsString: + return CVariant(m_simulator.toQString()); case IndexDescription: return CVariant(this->m_description); + case IndexName: + return CVariant(this->m_modelName); case IndexFileName: return CVariant(this->m_fileName); - case IndexIcao: - return m_icao.propertyByIndex(index.copyFrontRemoved()); + case IndexAircraftIcaoCode: + return m_aircraftIcao.propertyByIndex(index.copyFrontRemoved()); case IndexLivery: return m_livery.propertyByIndex(index.copyFrontRemoved()); case IndexCallsign: @@ -83,15 +98,24 @@ namespace BlackMisc case IndexModelString: this->m_modelString = variant.toQString(); break; - case IndexIcao: - this->m_icao.setPropertyByIndex(variant, index.copyFrontRemoved()); + case IndexAircraftIcaoCode: + this->m_aircraftIcao.setPropertyByIndex(variant, index.copyFrontRemoved()); break; case IndexLivery: this->m_livery.setPropertyByIndex(variant, index.copyFrontRemoved()); break; + case IndexDistributor: + this->m_distributor.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; case IndexDescription: this->m_description = variant.toQString(); break; + case IndexSimulatorInfo: + this->m_simulator.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexName: + this->m_modelName = variant.toQString(); + break; case IndexCallsign: this->m_callsign.setPropertyByIndex(variant, index.copyFrontRemoved()); break; @@ -101,30 +125,67 @@ namespace BlackMisc case IndexModelType: this->m_modelType = static_cast(variant.toInt()); break; + case IndexModelMode: + this->m_modelMode = static_cast(variant.toInt()); + break; default: CValueObject::setPropertyByIndex(variant, index); break; } } - const CAircraftIcaoCode &CAircraftModel::getAircraftIcaoCode() const + bool CAircraftModel::setAircraftIcaoCode(const CAircraftIcaoCode &aircraftIcaoCode) { - return m_icao.getAircraftIcaoCode(); + if (this->m_aircraftIcao == aircraftIcaoCode) { return false; } + this->m_aircraftIcao = aircraftIcaoCode; + return true; } - const CAirlineIcaoCode &CAircraftModel::getAirlineIcaoCode() const + void CAircraftModel::setAircraftIcaoDesignator(const QString &designator) { - return m_icao.getAirlineIcaoCode(); + this->m_aircraftIcao.setDesignator(designator); + } + + void CAircraftModel::setAircraftIcaoCodes(const CAircraftIcaoCode &aircraftIcaoCode, const CAirlineIcaoCode &airlineIcaoCode) + { + m_aircraftIcao = aircraftIcaoCode; + m_livery.setAirlineIcaoCode(airlineIcaoCode); + } + + bool CAircraftModel::hasAircraftAndAirlineDesignator() const + { + return this->m_aircraftIcao.hasDesignator() && this->m_livery.hasValidAirlineDesignator(); + } + + bool CAircraftModel::hasAircraftDesignator() const + { + return this->m_aircraftIcao.hasDesignator(); + } + + bool CAircraftModel::hasAirlineDesignator() const + { + return this->m_livery.hasValidAirlineDesignator(); } void CAircraftModel::updateMissingParts(const CAircraftModel &model) { - if (this->m_modelString.isEmpty()) { this->m_modelString = model.getModelString(); } - if (this->m_description.isEmpty()) { this->m_description = model.getDescription(); } - if (this->m_fileName.isEmpty()) { this->m_fileName = model.getFileName(); } - if (this->m_callsign.isEmpty()) { this->m_callsign = model.getCallsign(); } + if (this->m_modelString.isEmpty()) { this->setModelString(model.getModelString()); } + if (this->m_description.isEmpty()) { this->setDescription(model.getDescription()); } + if (this->m_fileName.isEmpty()) { this->setFileName(model.getFileName()); } + if (this->m_callsign.isEmpty()) { this->setCallsign(model.getCallsign()); } if (this->m_modelType == static_cast(TypeUnknown)) { this->m_modelType = model.getModelType(); } - this->m_icao.updateMissingParts(model.getIcao()); + if (this->m_simulator.isUnspecified()) + { + this->setSimulatorInfo(model.getSimulatorInfo()); + } + else + { + this->m_simulator.add(model.getSimulatorInfo()); + } + + this->m_livery.updateMissingParts(model.getLivery()); + this->m_aircraftIcao.updateMissingParts(model.getAircraftIcaoCode()); + this->m_distributor.updateMissingParts(model.getDistributor()); } bool CAircraftModel::hasQueriedModelString() const @@ -137,6 +198,11 @@ namespace BlackMisc return this->m_modelType == TypeManuallySet && this->hasModelString(); } + bool CAircraftModel::hasValidSimulator() const + { + return m_simulator.isAnySimulator(); + } + bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const { if (sensitivity == Qt::CaseSensitive) @@ -150,13 +216,29 @@ namespace BlackMisc } } + CStatusMessageList CAircraftModel::validate(bool withNestedObjects) const + { + static const CLogCategoryList cats( { CLogCategory(this->getClassName()), CLogCategory::validation()}); + CStatusMessageList msgs; + if (!hasModelString()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Model: missing model string (aka key)")); } + if (!hasValidSimulator()) {msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Model: no simulator set")); } + if (!hasDescription()) {msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, "Model: no description")); } + if (withNestedObjects) + { + msgs.push_back(m_aircraftIcao.validate()); + msgs.push_back(m_livery.validate()); + msgs.push_back(m_distributor.validate()); + } + return msgs; + } + QString CAircraftModel::modelTypeToString(CAircraftModel::ModelType type) { switch (type) { case TypeQueriedFromNetwork: return "queried"; case TypeModelMatching: return "matching"; - case TypeModelMapping: return "mapping"; + case TypeDatabaseEntry: return "database"; case TypeModelMatchingDefaultModel: return "map. default"; case TypeOwnSimulatorModel: return "own simulator"; case TypeManuallySet: return "set"; @@ -165,38 +247,49 @@ namespace BlackMisc } } - CAircraftModel CAircraftModel::fromDatabaseJson(const QJsonObject &json) + CAircraftModel::ModelMode CAircraftModel::modelModeFromString(const QString &mode) { - QJsonArray inner = json["cell"].toArray(); - Q_ASSERT_X(!inner.isEmpty(), Q_FUNC_INFO, "Missing JSON"); - if (inner.isEmpty()) { return CAircraftModel(); } - - // int i = 0; - - int i = 0; - int dbKey(inner.at(i++).toInt(-1)); - QString modelString(inner.at(i++).toString()); - QString distributorKey(inner.at(i++).toString()); - QString liveryDescription(inner.at(i++).toString()); - QString modelDescription; - - CAircraftIcaoData aircraftIcao; - CAirlineIcaoCode airlineIcao; - CLivery livery; - CDistributor distributor(distributorKey, "", "", ""); - - bool fsx = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); - bool fs9 = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); - bool xp = CDatastoreUtility::dbBoolStringToBool(inner.at(i++).toString()); - CSimulatorInfo simInfo(fsx, fs9, xp); - - CAircraftModel model( - modelString, CAircraftModel::TypeModelMapping, modelDescription, aircraftIcao, livery - ); - model.setDbKey(dbKey); - model.setSimulatorInfo(simInfo); - return model; + if (mode.isEmpty() || mode.startsWith('I', Qt::CaseInsensitive)) { return Include;} + if (mode.startsWith('E', Qt::CaseInsensitive)) { return Exclude; } + Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode"); + return Include; // default } + QString CAircraftModel::modelModeToString(CAircraftModel::ModelMode mode) + { + switch (mode) + { + case Include: return "Include"; + case Exclude: return "Exclude"; + default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode"); + } + return "Include"; + } + + CAircraftModel CAircraftModel::fromDatabaseJson(const QJsonObject &json, const QString prefix) + { + QString modelString(json.value(prefix + "simkey").toString()); + QString modelDescription(json.value(prefix + "description").toString()); + QString modelName(json.value(prefix + "name").toString()); + QString modelMode(json.value(prefix + "mode").toString()); + + bool fsx = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "simfsx").toString()); + bool fs9 = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "simfs9").toString()); + bool xp = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "simxplane").toString()); + bool p3d = CDatastoreUtility::dbBoolStringToBool(json.value(prefix + "simp3d").toString()); + + CAircraftIcaoCode aircraftIcao(CAircraftIcaoCode::fromDatabaseJson(json, "ac_")); + CLivery livery(CLivery::fromDatabaseJson(json, "liv_")); + CDistributor distributor(CDistributor::fromDatabaseJson(json, "dist_")); + + CSimulatorInfo simInfo(fsx, fs9, xp, p3d); + CAircraftModel model( + modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription, aircraftIcao, livery + ); + model.setDistributor(distributor); + model.setModelMode(modelModeFromString(modelMode)); + model.setKeyAndTimestampFromDatabaseJson(json, prefix); + return model; + } } // namespace } // namespace diff --git a/src/blackmisc/simulation/aircraftmodel.h b/src/blackmisc/simulation/aircraftmodel.h index eb0bc5285..800f60bd1 100644 --- a/src/blackmisc/simulation/aircraftmodel.h +++ b/src/blackmisc/simulation/aircraftmodel.h @@ -14,11 +14,12 @@ #include "blackmisc/blackmiscexport.h" #include "blackmisc/simulation/simulatorinfo.h" -#include "blackmisc/aviation/aircraft.h" -#include "blackmisc/aviation/aircrafticaodata.h" +#include "blackmisc/simulation/distributor.h" #include "blackmisc/aviation/livery.h" +#include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/network/user.h" #include "blackmisc/propertyindex.h" +#include namespace BlackMisc { @@ -36,11 +37,20 @@ namespace BlackMisc { TypeUnknown, TypeQueriedFromNetwork, //!< model was queried by network protocol + TypeFsdData, //!< model based on FSD ICAO data TypeModelMatching, //!< model is result of model matching TypeModelMatchingDefaultModel, //!< a default model assigned by model matching - TypeModelMapping, //!< used along with mapping definition + TypeDatabaseEntry, //!< used along with mapping definition TypeManuallySet, //!< manually set, e.g. from GUI - TypeOwnSimulatorModel //!< represents own simulator model + TypeOwnSimulatorModel, //!< represents own simulator model + TypeVPilotRuleBased //!< based on a vPilot rule + }; + + //! Mode + enum ModelMode + { + Include, + Exclude }; //! Indexes @@ -48,12 +58,18 @@ namespace BlackMisc { IndexModelString = BlackMisc::CPropertyIndex::GlobalIndexCAircraftModel, IndexCallsign, + IndexName, IndexDescription, - IndexIcao, + IndexSimulatorInfo, + IndexSimulatorInfoAsString, + IndexAircraftIcaoCode, IndexLivery, + IndexDistributor, IndexFileName, IndexModelType, IndexModelTypeAsString, + IndexModelMode, + IndexModelModeAsString, IndexHasQueriedModelString }; @@ -64,10 +80,10 @@ namespace BlackMisc CAircraftModel(const QString &model, ModelType type); //! Constructor. - CAircraftModel(const QString &model, ModelType type, const QString &description, const BlackMisc::Aviation::CAircraftIcaoData &icao, const BlackMisc::Aviation::CLivery &livery = BlackMisc::Aviation::CLivery()); + CAircraftModel(const QString &model, ModelType type, const QString &description, const BlackMisc::Aviation::CAircraftIcaoCode &icao, const BlackMisc::Aviation::CLivery &livery = BlackMisc::Aviation::CLivery()); - //! Constructor - CAircraftModel(const BlackMisc::Aviation::CAircraft &aircraft); + //! Constructor. + CAircraftModel(const QString &model, ModelType type, CSimulatorInfo &simulator, const QString &name, const QString &description, const BlackMisc::Aviation::CAircraftIcaoCode &icao, const BlackMisc::Aviation::CLivery &livery = BlackMisc::Aviation::CLivery()); //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; @@ -88,7 +104,7 @@ namespace BlackMisc const QString &getModelString() const { return this->m_modelString; } //! Model string - void setModelString(const QString &modelString) { this->m_modelString = modelString; } + void setModelString(const QString &modelString) { this->m_modelString = modelString.trimmed().toUpper(); } //! Descriptive text const QString &getDescription() const { return this->m_description; } @@ -99,23 +115,35 @@ namespace BlackMisc //! Set queried model string void setQueriedModelString(const QString &model) { this->m_modelString = model; this->m_modelType = TypeQueriedFromNetwork; } - //! ICAO code - BlackMisc::Aviation::CAircraftIcaoData getIcao() const { return this->m_icao; } - - //! Set ICAO info - void setIcao(const BlackMisc::Aviation::CAircraftIcaoData &icao) { this->m_icao = icao; } - - //! \copydoc CAircraftIcaoData::hasAircraftAndAirlineDesignator - bool hasAircraftAndAirlineDesignator() const { return this->m_icao.hasAircraftAndAirlineDesignator(); } - - //! \copydoc CAircraftIcaoData::hasAircraftDesignator - bool hasAircraftDesignator() const { return this->m_icao.hasAircraftDesignator(); } - //! Aircraft ICAO code - const BlackMisc::Aviation::CAircraftIcaoCode &getAircraftIcaoCode() const; + const BlackMisc::Aviation::CAircraftIcaoCode &getAircraftIcaoCode() const { return this->m_aircraftIcao; } + + //! Aircraft ICAO code designator + const QString &getAircraftIcaoCodeDesignator() const { return this->m_aircraftIcao.getDesignator(); } //! Airline ICAO code - const BlackMisc::Aviation::CAirlineIcaoCode &getAirlineIcaoCode() const; + const BlackMisc::Aviation::CAirlineIcaoCode &getAirlineIcaoCode() const { return this->m_livery.getAirlineIcaoCode(); } + + //! Airline ICAO code designator + const QString &getAirlineIcaoCodeDesignator() const { return this->m_livery.getAirlineIcaoCode().getDesignator(); } + + //! Set aircraft ICAO code + bool setAircraftIcaoCode(const BlackMisc::Aviation::CAircraftIcaoCode &aircraftIcaoCode); + + //! Set aircraft ICAO code designator + void setAircraftIcaoDesignator(const QString &designator); + + //! Set ICAO codes + void setAircraftIcaoCodes(const BlackMisc::Aviation::CAircraftIcaoCode &aircraftIcaoCode, const BlackMisc::Aviation::CAirlineIcaoCode &airlineIcaoCode); + + //! Airline and aircraft designator? + bool hasAircraftAndAirlineDesignator() const; + + //! Has aircraft designator? + bool hasAircraftDesignator() const; + + //! Airline designator? + bool hasAirlineDesignator() const; //! Get livery const BlackMisc::Aviation::CLivery &getLivery() const { return m_livery; } @@ -126,6 +154,18 @@ namespace BlackMisc //! Livery available? bool hasLivery() const { return m_livery.hasCompleteData();} + //! Get distributor + const CDistributor &getDistributor() const { return m_distributor; } + + //! Set distributor + void setDistributor(const CDistributor &distributor) { m_distributor = distributor; } + + //! Name + const QString &getName() const { return this->m_modelName; } + + //! Name + void setName(const QString &name) { this->m_modelName = name.trimmed(); } + //! Model type ModelType getModelType() const { return m_modelType; } @@ -135,6 +175,15 @@ namespace BlackMisc //! Set type void setModelType(ModelType type) { this->m_modelType = type; } + //! Model mode + ModelMode getModelMode() const { return m_modelMode; } + + //! Model mode as string + QString getModelModeAsString() const { return modelModeToString(getModelMode()); } + + //! Set model mode + void setModelMode(ModelMode mode) { m_modelMode = mode; } + //! Simulator info CSimulatorInfo getSimulatorInfo() const { return this->m_simulator; } @@ -162,41 +211,65 @@ namespace BlackMisc //! Non empty model string bool hasModelString() const { return !m_modelString.isEmpty(); } + //! Description + bool hasDescription() const { return !m_description.isEmpty(); } + + //! Valid simulator + bool hasValidSimulator() const; + //! Matches model string? bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const; + //! Validate + BlackMisc::CStatusMessageList validate(bool withNestedObjects) const; + //! \copydoc CValueObject::convertToQString QString convertToQString(bool i18n = false) const; //! Model type static QString modelTypeToString(ModelType type); + //! Model mode + static ModelMode modelModeFromString(const QString &mode); + + //! Model mode + static QString modelModeToString(ModelMode mode); + //! From swift DB JSON - static CAircraftModel fromDatabaseJson(const QJsonObject &json); + static CAircraftModel fromDatabaseJson(const QJsonObject &json, const QString prefix = QString("mod_")); private: BLACK_ENABLE_TUPLE_CONVERSION(CAircraftModel) - BlackMisc::Aviation::CCallsign m_callsign; //!< aircraft's callsign if any - BlackMisc::Aviation::CAircraftIcaoData m_icao; //!< ICAO data if available - BlackMisc::Aviation::CLivery m_livery; //!< livery information - CSimulatorInfo m_simulator; //!< model for given simulator - QString m_modelString; //!< Simulator model string - QString m_description; //!< descriptive text - QString m_fileName; //!< file name - ModelType m_modelType = TypeUnknown; //!< model string is coming from ...? + BlackMisc::Aviation::CCallsign m_callsign; //!< aircraft's callsign if any + BlackMisc::Aviation::CAircraftIcaoCode m_aircraftIcao; //!< ICAO code if available + BlackMisc::Aviation::CLivery m_livery; //!< livery information + CSimulatorInfo m_simulator; //!< model for given simulator + CDistributor m_distributor; //!< who designed or distributed the model + QString m_modelString; //!< Simulator model string + QString m_modelName; //!< Model name + QString m_description; //!< descriptive text + QString m_fileName; //!< file name + ModelType m_modelType = TypeUnknown; //!< model string is coming representing ...? + ModelMode m_modelMode = Include; //!< model mode (include / exclude) }; } // namespace } // namespace BLACK_DECLARE_TUPLE_CONVERSION( BlackMisc::Simulation::CAircraftModel, ( + attr(o.m_dbKey), + attr(o.m_timestampMSecsSinceEpoch), attr(o.m_callsign), - attr(o.m_icao), + attr(o.m_aircraftIcao), + attr(o.m_livery), attr(o.m_simulator), + attr(o.m_distributor), attr(o.m_modelString, flags()), + attr(o.m_modelName), attr(o.m_description, flags()), attr(o.m_fileName, flags ()), - attr(o.m_modelType) + attr(o.m_modelType), + attr(o.m_modelMode) )) Q_DECLARE_METATYPE(BlackMisc::Simulation::CAircraftModel) diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index 4df53740e..703e9e88e 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -38,8 +38,70 @@ namespace BlackMisc CAircraftModel CAircraftModelList::findFirstByModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const { - CAircraftModelList ml = findByModelString(modelString, sensitivity); - return ml.frontOrDefault(); + return this->findFirstBy([ = ](const CAircraftModel & model) + { + return model.matchesModelString(modelString, sensitivity); + }); + } + + CAircraftModelList CAircraftModelList::findByIcaoDesignators(const CAircraftIcaoCode &aircraftIcaoCode, const CAirlineIcaoCode &airlineIcaoCode) const + { + const QString aircraft(aircraftIcaoCode.getDesignator()); + const QString airline(airlineIcaoCode.getDesignator()); + + if (airline.isEmpty()) + { + return this->findBy([ = ](const CAircraftModel & model) + { + return model.getAircraftIcaoCode().getDesignator() == aircraft; + }); + } + if (aircraft.isEmpty()) + { + return this->findBy([ = ](const CAircraftModel & model) + { + return model.getAirlineIcaoCode().getDesignator() == airline; + }); + } + return this->findBy([ = ](const CAircraftModel & model) + { + return model.getAirlineIcaoCode().getDesignator() == airline && + model.getAircraftIcaoCode().getDesignator() == aircraft; + }); + } + + CAircraftModelList CAircraftModelList::findByAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) const + { + if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); } + return this->findBy([ = ](const CAircraftModel & model) + { + if (!model.getAircraftIcaoCode().matchesDesignator(aircraftDesignator)) { return false; } + return model.getLivery().matchesCombinedCode(combinedCode); + }); + } + + void CAircraftModelList::setSimulatorInfo(const CSimulatorInfo &info) + { + for (CAircraftModel &model : (*this)) + { + model.setSimulatorInfo(info); + } + } + + int CAircraftModelList::keepModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) + { + int cs = this->size(); + (*this) = (findByModelStrings(modelStrings, sensitivity)); + int d = cs - this->size(); + return d; + } + + int CAircraftModelList::removeModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) + { + int cs = this->size(); + (*this) = (findByNotInModelStrings(modelStrings, sensitivity)); + int d = cs - this->size(); + return d; } CAircraftModelList CAircraftModelList::findModelsStartingWith(const QString &modelString, Qt::CaseSensitivity sensitivity) const @@ -50,6 +112,22 @@ namespace BlackMisc }); } + CAircraftModelList CAircraftModelList::findByModelStrings(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) const + { + return this->findBy([ = ](const CAircraftModel & model) + { + return modelStrings.contains(model.getModelString(), sensitivity); + }); + } + + CAircraftModelList CAircraftModelList::findByNotInModelStrings(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) const + { + return this->findBy([ = ](const CAircraftModel & model) + { + return !modelStrings.contains(model.getModelString(), sensitivity); + }); + } + QStringList CAircraftModelList::getSortedModelStrings() const { QStringList ms; diff --git a/src/blackmisc/simulation/aircraftmodellist.h b/src/blackmisc/simulation/aircraftmodellist.h index 0841c5985..8b3940e33 100644 --- a/src/blackmisc/simulation/aircraftmodellist.h +++ b/src/blackmisc/simulation/aircraftmodellist.h @@ -49,6 +49,29 @@ namespace BlackMisc //! Find models starting with CAircraftModelList findModelsStartingWith(const QString &modelString, Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive) const; + //! Find by a given list of models by strings + CAircraftModelList findByModelStrings(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) const; + + //! Find by a given list of models trings + CAircraftModelList findByNotInModelStrings(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity) const; + + //! Find by model string + CAircraftModelList findByIcaoDesignators(const BlackMisc::Aviation::CAircraftIcaoCode &aircraftIcaoCode, const BlackMisc::Aviation::CAirlineIcaoCode &airlineIcaoCode) const; + + //! Find by model string + CAircraftModelList findByAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) const; + + //! Set simulator for all elements + void setSimulatorInfo(const BlackMisc::Simulation::CSimulatorInfo &info); + + //! Keep only those models with given model strings + //! \return number of elements removed + int keepModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity); + + //! Remove those models with given model strings + //! \return number of elements removed + int removeModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity); + //! Model strings QStringList getSortedModelStrings() const; }; diff --git a/src/blackmisc/simulation/simulatedaircraft.cpp b/src/blackmisc/simulation/simulatedaircraft.cpp index f0b1e0438..54e99a30c 100644 --- a/src/blackmisc/simulation/simulatedaircraft.cpp +++ b/src/blackmisc/simulation/simulatedaircraft.cpp @@ -23,8 +23,13 @@ namespace BlackMisc init(); } - CSimulatedAircraft::CSimulatedAircraft(const CAircraft &aircraft, const CAircraftModel &model, const CClient &client) : - CValueObject(aircraft), m_model(model), m_client(client) + CSimulatedAircraft::CSimulatedAircraft(const CAircraftModel &model) : m_model(model) + { + init(); + } + + CSimulatedAircraft::CSimulatedAircraft(const CCallsign &callsign, const CUser &user, const CAircraftSituation &situation) : + m_callsign(callsign), m_pilot(user), m_situation(situation) { init(); } @@ -34,10 +39,209 @@ namespace BlackMisc // sync some values, order here is crucial // set get/set thing here updates the redundant data (e.g. livery / model.livery) this->setCallsign(this->getCallsign()); - this->setIcaoInfo(this->getIcaoInfo()); + this->setIcaoCodes(this->getAircraftIcaoCode(), this->getAirlineIcaoCode()); this->setLivery(this->getLivery()); this->setModel(this->getModel()); - this->setPilot(this->hasValidRealName() ? this->getPilot() : this->getClient().getUser()); + } + + void CSimulatedAircraft::setCockpit(const CComSystem &com1, const CComSystem &com2, const CTransponder &transponder) + { + this->setCom1System(com1); + this->setCom2System(com2); + this->setTransponder(transponder); + } + + void CSimulatedAircraft::setCockpit(const CComSystem &com1, const CComSystem &com2, int transponderCode, CTransponder::TransponderMode transponderMode) + { + this->setCom1System(com1); + this->setCom2System(com2); + this->m_transponder.setTransponderCode(transponderCode); + this->m_transponder.setTransponderMode(transponderMode); + } + + bool CSimulatedAircraft::hasChangedCockpitData(const CComSystem &com1, const CComSystem &com2, const CTransponder &transponder) const + { + return this->getCom1System() != com1 || this->getCom2System() != com2 || this->getTransponder() != transponder; + } + + bool CSimulatedAircraft::hasSameComData(const CComSystem &com1, const CComSystem &com2, const CTransponder &transponder) + { + return this->getCom1System() == com1 && this->getCom2System() == com2 && this->getTransponder() == transponder; + } + + bool CSimulatedAircraft::isValidForLogin() const + { + if (this->m_callsign.asString().isEmpty()) { return false; } + if (!this->m_pilot.isValid()) { return false; } + return true; + } + + void CSimulatedAircraft::setSituation(const CAircraftSituation &situation) + { + m_situation = situation; + m_situation.setCallsign(this->getCallsign()); + } + + const CAircraftIcaoCode &CSimulatedAircraft::getAircraftIcaoCode() const + { + return m_model.getAircraftIcaoCode(); + } + + void CSimulatedAircraft::setPilot(const Network::CUser &user) + { + m_pilot = user; + this->m_pilot.setCallsign(this->m_callsign); + } + + const QString &CSimulatedAircraft::getAircraftIcaoCodeDesignator() const + { + return getAircraftIcaoCode().getDesignator(); + } + + const QString &CSimulatedAircraft::getAircraftIcaoCombinedType() const + { + return getAircraftIcaoCode().getCombinedType(); + } + + bool CSimulatedAircraft::setIcaoCodes(const CAircraftIcaoCode &aircraftIcaoCode, const CAirlineIcaoCode &airlineIcaoCode) + { + bool c = this->m_model.setAircraftIcaoCode(aircraftIcaoCode); + return m_livery.setAirlineIcaoCode(airlineIcaoCode) || c; + } + + const CAirlineIcaoCode &CSimulatedAircraft::getAirlineIcaoCode() const + { + return m_livery.getAirlineIcaoCode(); + } + + const QString &CSimulatedAircraft::getAirlineIcaoCodeDesignator() const + { + return getAircraftIcaoCode().getDesignator(); + } + + void CSimulatedAircraft::setAircraftIcaoDesignator(const QString &designator) + { + this->m_model.setAircraftIcaoDesignator(designator); + } + + bool CSimulatedAircraft::hasAircraftDesignator() const + { + return this->getAircraftIcaoCode().hasDesignator(); + } + + bool CSimulatedAircraft::hasAircraftAndAirlineDesignator() const + { + return this->getAircraftIcaoCode().hasDesignator() && m_livery.hasValidAirlineDesignator(); + } + + const CComSystem CSimulatedAircraft::getComSystem(CComSystem::ComUnit unit) const + { + switch (unit) + { + case CComSystem::Com1: return this->getCom1System(); + case CComSystem::Com2: return this->getCom2System(); + default: break; + } + Q_ASSERT(false); + return CComSystem(); // avoid warning + } + + void CSimulatedAircraft::setComSystem(const CComSystem &com, CComSystem::ComUnit unit) + { + switch (unit) + { + case CComSystem::Com1: this->setCom1System(com); break; + case CComSystem::Com2: this->setCom2System(com); break; + } + } + + bool CSimulatedAircraft::setCom1ActiveFrequency(const CFrequency &frequency) + { + if (!CComSystem::isValidComFrequency(frequency)) { return false; } + this->m_com1system.setFrequencyActive(frequency); + return true; + } + + bool CSimulatedAircraft::setCom2ActiveFrequency(const CFrequency &frequency) + { + if (!CComSystem::isValidComFrequency(frequency)) { return false; } + this->m_com2system.setFrequencyActive(frequency); + return true; + } + + bool CSimulatedAircraft::setComActiveFrequency(const CFrequency &frequency, CComSystem::ComUnit unit) + { + if (!CComSystem::isValidComFrequency(frequency)) { return false; } + switch (unit) + { + case CComSystem::Com1: return this->setCom1ActiveFrequency(frequency); + case CComSystem::Com2: return this->setCom2ActiveFrequency(frequency); + } + return false; + } + + void CSimulatedAircraft::initComSystems() + { + CComSystem com1("COM1", CPhysicalQuantitiesConstants::FrequencyUnicom(), CPhysicalQuantitiesConstants::FrequencyUnicom()); + CComSystem com2("COM2", CPhysicalQuantitiesConstants::FrequencyUnicom(), CPhysicalQuantitiesConstants::FrequencyUnicom()); + this->setCom1System(com1); + this->setCom2System(com2); + } + + void CSimulatedAircraft::initTransponder() + { + CTransponder xpdr(7000, CTransponder::StateStandby); + this->setTransponder(xpdr); + } + + CAircraftLights CSimulatedAircraft::getLights() const + { + return m_parts.getLights(); + } + + void CSimulatedAircraft::setParts(const CAircraftParts &parts) + { + m_parts = parts; + m_parts.setCallsign(this->getCallsign()); + } + + void CSimulatedAircraft::setLights(CAircraftLights &lights) + { + m_parts.setLights(lights); + } + + void CSimulatedAircraft::setAllLightsOn() + { + m_parts.setAllLightsOn(); + } + + void CSimulatedAircraft::setAllLightsOff() + { + m_parts.setAllLightsOff(); + } + + bool CSimulatedAircraft::isVtol() const + { + return getAircraftIcaoCode().isVtol(); + } + + QString CSimulatedAircraft::getCombinedIcaoLiveryString() const + { + if (this->hasAircraftAndAirlineDesignator()) + { + QString s("%1 (%2)"); + return s.arg(getAircraftIcaoCodeDesignator()).arg(getAirlineIcaoCodeDesignator()); + } + + if (!this->hasAircraftDesignator()) + { + return getLivery().getCombinedCode(); + } + else + { + QString s("%1 (%2)"); + return s.arg(getAircraftIcaoCodeDesignator()).arg(getLivery().getCombinedCode()); + } } CVariant CSimulatedAircraft::propertyByIndex(const BlackMisc::CPropertyIndex &index) const @@ -48,8 +252,6 @@ namespace BlackMisc { case IndexModel: return this->m_model.propertyByIndex(index.copyFrontRemoved()); - case IndexClient: - return this->m_client.propertyByIndex(index.copyFrontRemoved()); case IndexEnabled: return CVariant::fromValue(this->isEnabled()); case IndexRendered: @@ -58,8 +260,34 @@ namespace BlackMisc return CVariant::fromValue(this->isPartsSynchronized()); case IndexFastPositionUpdates: return CVariant::fromValue(this->fastPositionUpdates()); + case IndexCallsign: + return this->m_callsign.propertyByIndex(index.copyFrontRemoved()); + case IndexPilot: + return this->m_pilot.propertyByIndex(index.copyFrontRemoved()); + case IndexDistanceToOwnAircraft: + return this->m_distanceToOwnAircraft.propertyByIndex(index.copyFrontRemoved()); + case IndexCom1System: + return this->m_com1system.propertyByIndex(index.copyFrontRemoved()); + case IndexCom2System: + return this->m_com2system.propertyByIndex(index.copyFrontRemoved()); + case IndexTransponder: + return this->m_transponder.propertyByIndex(index.copyFrontRemoved()); + case IndexSituation: + return this->m_situation.propertyByIndex(index.copyFrontRemoved()); + case IndexAircraftIcaoCode: + return this->getAircraftIcaoCode().propertyByIndex(index.copyFrontRemoved()); + case IndexLivery: + return this->m_livery.propertyByIndex(index.copyFrontRemoved()); + case IndexParts: + return this->m_parts.propertyByIndex(index.copyFrontRemoved()); + case IndexIsVtol: + return CVariant::fromValue(this->isVtol()); + case IndexCombinedIcaoLiveryString: + return CVariant::fromValue(this->getCombinedIcaoLiveryString()); default: - return CAircraft::propertyByIndex(index); + return (ICoordinateGeodetic::canHandleIndex(index)) ? + ICoordinateGeodetic::propertyByIndex(index) : + CValueObject::propertyByIndex(index); } } @@ -69,13 +297,40 @@ namespace BlackMisc ColumnIndex i = index.frontCasted(); switch (i) { + case IndexCallsign: + this->m_callsign.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexPilot: + this->m_pilot.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexDistanceToOwnAircraft: + this->m_distanceToOwnAircraft.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexCom1System: + this->m_com1system.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexCom2System: + this->m_com2system.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexTransponder: + this->m_transponder.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexAircraftIcaoCode: + this->m_livery.setPropertyByIndex(variant, index); // intentionally not removing front, delegating + break; + case IndexLivery: + this->m_livery.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexSituation: + this->m_situation.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; + case IndexParts: + this->m_parts.setPropertyByIndex(variant, index.copyFrontRemoved()); + break; case IndexModel: this->m_model.setPropertyByIndex(variant, index.copyFrontRemoved()); this->setModel(this->m_model); // sync some values break; - case IndexClient: - this->m_client.setPropertyByIndex(variant, index.copyFrontRemoved()); - break; case IndexEnabled: this->m_enabled = variant.toBool(); break; @@ -89,7 +344,7 @@ namespace BlackMisc this->m_fastPositionUpdates = variant.toBool(); break; default: - CAircraft::setPropertyByIndex(variant, index); + CValueObject::setPropertyByIndex(variant, index); break; } } @@ -99,7 +354,7 @@ namespace BlackMisc // sync the callsigns this->m_model = model; this->setCallsign(this->hasValidCallsign() ? this->getCallsign() : model.getCallsign()); - this->setIcaoInfo(model.getIcao()); + this->setIcaoCodes(model.getAircraftIcaoCode(), model.getAirlineIcaoCode()); } void CSimulatedAircraft::setModelString(const QString &modelString) @@ -109,54 +364,35 @@ namespace BlackMisc void CSimulatedAircraft::setCallsign(const CCallsign &callsign) { - CAircraft::setCallsign(callsign); // also sets correct hint - this->m_model.setCallsign(getCallsign()); - this->m_client.setUserCallsign(getCallsign()); + this->m_callsign = callsign; + this->m_model.setCallsign(callsign); } - void CSimulatedAircraft::setIcaoInfo(const CAircraftIcaoData &icao) + bool CSimulatedAircraft::isActiveFrequencyWithin8_33kHzChannel(const CFrequency &comFrequency) const { - // snyc ICAO info - CAircraftIcaoData newIcao(icao); - newIcao.updateMissingParts(this->getIcaoInfo()); - newIcao.updateMissingParts(this->getModel().getIcao()); - - // now we have a superset of ICAO data - this->m_model.setIcao(newIcao); - CAircraft::setIcaoInfo(newIcao); + return this->m_com1system.isActiveFrequencyWithin8_33kHzChannel(comFrequency) || + this->m_com2system.isActiveFrequencyWithin8_33kHzChannel(comFrequency); } - void CSimulatedAircraft::setLivery(const CLivery &livery) + bool CSimulatedAircraft::isActiveFrequencyWithin25kHzChannel(const CFrequency &comFrequency) const { - this->m_model.setLivery(livery); - CAircraft::setLivery(livery); - } - - void CSimulatedAircraft::setPilot(const CUser &user) - { - this->m_client.setUser(user); - CAircraft::setPilot(user); - } - - void CSimulatedAircraft::setClient(const CClient &client) - { - Q_ASSERT(client.getCallsign() == this->getCallsign()); - m_client = client; - } - - //! \todo Smarter way to do this? - void CSimulatedAircraft::setAircraft(const CAircraft &aircraft) - { - static_cast(*this) = aircraft; - this->m_model.setCallsign(aircraft.getCallsign()); - this->m_client.setAircraftModel(this->getModel()); - this->m_client.setUser(aircraft.getPilot()); - this->m_client.setUserCallsign(aircraft.getCallsign()); + return this->m_com1system.isActiveFrequencyWithin25kHzChannel(comFrequency) || + this->m_com2system.isActiveFrequencyWithin25kHzChannel(comFrequency); } QString CSimulatedAircraft::convertToQString(bool i18n) const { - QString s = CAircraft::convertToQString(i18n); + QString s(this->m_callsign.toQString(i18n)); + s += " "; + s += this->m_pilot.toQString(i18n); + s += " "; + s += this->m_situation.toQString(i18n); + s += " "; + s += this->m_com1system.toQString(i18n); + s += " "; + s += this->m_com2system.toQString(i18n); + s += " "; + s += this->m_transponder.toQString(i18n); s += " enabled: "; s += BlackMisc::boolToYesNo(this->isEnabled()); s += " "; @@ -164,8 +400,6 @@ namespace BlackMisc s += BlackMisc::boolToYesNo(this->isRendered()); s += " "; s += this->m_model.toQString(i18n); - s += " "; - s += this->m_client.toQString(i18n); return s; } diff --git a/src/blackmisc/simulation/simulatedaircraft.h b/src/blackmisc/simulation/simulatedaircraft.h index 222e2823a..3d4be087c 100644 --- a/src/blackmisc/simulation/simulatedaircraft.h +++ b/src/blackmisc/simulation/simulatedaircraft.h @@ -13,24 +13,48 @@ #define BLACKMISC_SIMULATION_SIMULATEDAIRCRAFT_H #include "blackmisc/blackmiscexport.h" -#include "blackmisc/aviation/aircraft.h" #include "aircraftmodel.h" +#include "blackmisc/aviation/aircraftsituation.h" +#include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/aviation/selcal.h" +#include "blackmisc/aviation/transponder.h" +#include "blackmisc/aviation/comsystem.h" +#include "blackmisc/aviation/aircraftparts.h" +#include "blackmisc/aviation/livery.h" +#include "blackmisc/valueobject.h" +#include "blackmisc/namevariantpairlist.h" +#include "blackmisc/propertyindex.h" #include "blackmisc/network/client.h" +#include "blackmisc/network/user.h" +#include "blackmisc/blackmiscfreefunctions.h" namespace BlackMisc { namespace Simulation { //! Comprehensive information of an aircraft - //! \sa CAircraft - class BLACKMISC_EXPORT CSimulatedAircraft : public CValueObject + class BLACKMISC_EXPORT CSimulatedAircraft : + public CValueObject, + public BlackMisc::Geo::ICoordinateWithRelativePosition { public: //! Properties by index enum ColumnIndex { - IndexModel = BlackMisc::CPropertyIndex::GlobalIndexCSimulatedAircraft, - IndexClient, + IndexCallsign = BlackMisc::CPropertyIndex::GlobalIndexCSimulatedAircraft, + IndexPilot, + IndexDistanceToOwnAircraft, + IndexCom1System, + IndexCom2System, + IndexTransponder, + IndexSituation, + IndexAircraftIcaoCode, + IndexLivery, + IndexParts, + IndexIsVtol, + IndexCombinedIcaoLiveryString, + IndexModel, IndexEnabled, IndexRendered, IndexPartsSynchronized, @@ -41,9 +65,235 @@ namespace BlackMisc CSimulatedAircraft(); //! Constructor. - CSimulatedAircraft(const BlackMisc::Aviation::CAircraft &aircraft, - const BlackMisc::Simulation::CAircraftModel &model = {}, - const BlackMisc::Network::CClient &client = {}); + CSimulatedAircraft(const BlackMisc::Simulation::CAircraftModel &model); + + //! Constructor. + CSimulatedAircraft(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Network::CUser &user, const BlackMisc::Aviation::CAircraftSituation &situation); + + //! \copydoc CValueObject::toIcon() + BlackMisc::CIcon toIcon() const { return this->m_callsign.toIcon(); } + + //! Get callsign. + const BlackMisc::Aviation::CCallsign &getCallsign() const { return m_callsign; } + + //! Get callsign. + QString getCallsignAsString() const { return m_callsign.asString(); } + + //! Get situation. + const BlackMisc::Aviation::CAircraftSituation &getSituation() const { return m_situation; } + + //! Set situation. + void setSituation(const BlackMisc::Aviation::CAircraftSituation &situation); + + //! Get user + const BlackMisc::Network::CUser &getPilot() const { return m_pilot; } + + //! Get user's real name + QString getPilotRealname() const { return m_pilot.getRealName(); } + + //! Get user's real id + QString getPilotId() { return m_pilot.getId(); } + + //! Get aircraft ICAO info + const BlackMisc::Aviation::CAircraftIcaoCode &getAircraftIcaoCode() const; + + //! Aircraft ICAO code designator + const QString &getAircraftIcaoCodeDesignator() const; + + //! Aircraft ICAO combined code + const QString &getAircraftIcaoCombinedType() const; + + //! Set aicraft ICAO code + bool setAircraftIcaoCode(const BlackMisc::Aviation::CAircraftIcaoCode &aircraftIcaoCode) { return m_model.setAircraftIcaoCode(aircraftIcaoCode);} + + //! Set ICAO info + bool setIcaoCodes(const BlackMisc::Aviation::CAircraftIcaoCode &aircraftIcaoCode, const BlackMisc::Aviation::CAirlineIcaoCode &airlineIcaoCode); + + //! Get livery + const BlackMisc::Aviation::CLivery &getLivery() const { return m_livery; } + + //! Airline ICAO code if any + const BlackMisc::Aviation::CAirlineIcaoCode &getAirlineIcaoCode() const; + + //! Airline ICAO code designator + const QString &getAirlineIcaoCodeDesignator() const; + + //! Livery + virtual void setLivery(const BlackMisc::Aviation::CLivery &livery) { this->m_livery = livery; } + + //! Set aircraft ICAO designator + virtual void setAircraftIcaoDesignator(const QString &designator); + + //! Has valid realname? + bool hasValidRealName() const { return this->m_pilot.hasValidRealName(); } + + //! Has valid id? + bool hasValidId() const { return this->m_pilot.hasValidId(); } + + //! Valid designator? + bool hasAircraftDesignator() const; + + //! Valid airline designator + bool hasAirlineDesignator() const { return this->m_livery.hasValidAirlineDesignator(); } + + //! Valid designators? + bool hasAircraftAndAirlineDesignator() const; + + //! Valid callsign + bool hasValidCallsign() const { return BlackMisc::Aviation::CCallsign::isValidCallsign(this->getCallsign().asString()); } + + //! Get position + BlackMisc::Geo::CCoordinateGeodetic getPosition() const { return this->m_situation.getPosition(); } + + //! Set position + void setPosition(const BlackMisc::Geo::CCoordinateGeodetic &position) { this->m_situation.setPosition(position); } + + //! Get altitude + const BlackMisc::Aviation::CAltitude &getAltitude() const { return this->m_situation.getAltitude(); } + + //! Set altitude + void setAltitude(const BlackMisc::Aviation::CAltitude &altitude) { this->m_situation.setAltitude(altitude); } + + //! Get groundspeed + const BlackMisc::PhysicalQuantities::CSpeed &getGroundSpeed() const { return this->m_situation.getGroundSpeed(); } + + //! \copydoc ICoordinateGeodetic::latitude + virtual const BlackMisc::Geo::CLatitude &latitude() const override { return this->m_situation.latitude(); } + + //! \copydoc ICoordinateGeodetic::longitude + virtual const BlackMisc::Geo::CLongitude &longitude() const override { return this->m_situation.longitude(); } + + //! \copydoc ICoordinateGeodetic::geodeticHeight + //! \remarks this should be used for elevation as depicted here: http://en.wikipedia.org/wiki/Altitude#mediaviewer/File:Vertical_distances.svg + const BlackMisc::PhysicalQuantities::CLength &geodeticHeight() const override { return this->m_situation.geodeticHeight(); } + + //! Elevation + //! \sa geodeticHeight + const BlackMisc::PhysicalQuantities::CLength getElevation() const { return this->geodeticHeight(); } + + //! Elevation + //! \sa setGeodeticHeight + void setElevation(const BlackMisc::PhysicalQuantities::CLength &elevation) { return this->m_situation.setElevation(elevation); } + + //! Get heading + const BlackMisc::Aviation::CHeading &getHeading() const { return this->m_situation.getHeading(); } + + //! Get pitch + const BlackMisc::PhysicalQuantities::CAngle &getPitch() const { return this->m_situation.getPitch(); } + + //! Get bank + const BlackMisc::PhysicalQuantities::CAngle &getBank() const { return this->m_situation.getBank(); } + + //! Get COM1 system + const BlackMisc::Aviation::CComSystem &getCom1System() const { return this->m_com1system; } + + //! Get COM2 system + const BlackMisc::Aviation::CComSystem &getCom2System() const { return this->m_com2system; } + + //! Get COM unit + const BlackMisc::Aviation::CComSystem getComSystem(BlackMisc::Aviation::CComSystem::ComUnit unit) const; + + //! Set COM unit + void setComSystem(const BlackMisc::Aviation::CComSystem &com, BlackMisc::Aviation::CComSystem::ComUnit unit); + + //! Set COM1 system + void setCom1System(const BlackMisc::Aviation::CComSystem &comSystem) { this->m_com1system = comSystem; } + + //! Set COM2 system + void setCom2System(const BlackMisc::Aviation::CComSystem &comSystem) { this->m_com2system = comSystem; } + + //! Set COM1 frequency + bool setCom1ActiveFrequency(const BlackMisc::PhysicalQuantities::CFrequency &frequency); + + //! Set COM2 frequency + bool setCom2ActiveFrequency(const BlackMisc::PhysicalQuantities::CFrequency &frequency); + + //! Set COM frequency + bool setComActiveFrequency(const BlackMisc::PhysicalQuantities::CFrequency &frequency, BlackMisc::Aviation::CComSystem::ComUnit unit); + + //! Given SELCAL selected? + bool isSelcalSelected(const BlackMisc::Aviation::CSelcal &selcal) const { return this->m_selcal == selcal; } + + //! Valid SELCAL? + bool hasValidSelcal() const { return this->m_selcal.isValid(); } + + //! SELCAL + const BlackMisc::Aviation::CSelcal getSelcal() const { return m_selcal; } + + //! Cockpit data + void setCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder); + + //! Cockpit data + void setCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, int transponderCode, BlackMisc::Aviation::CTransponder::TransponderMode mode); + + //! Own SELCAL code + void setSelcal(const BlackMisc::Aviation::CSelcal &selcal) { this->m_selcal = selcal; } + + //! Changed cockpit data? + bool hasChangedCockpitData(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder) const; + + //! Identical COM system? + bool hasSameComData(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder); + + //! Is any (COM1/2) active frequency within 8.3383kHz channel? + bool isActiveFrequencyWithin8_33kHzChannel(const BlackMisc::PhysicalQuantities::CFrequency &comFrequency) const; + + //! Is any (COM1/2) active frequency within 25kHz channel? + bool isActiveFrequencyWithin25kHzChannel(const BlackMisc::PhysicalQuantities::CFrequency &comFrequency) const; + + //! Get transponder + const BlackMisc::Aviation::CTransponder &getTransponder() const { return this->m_transponder; } + + //! Set transponder + void setTransponder(const BlackMisc::Aviation::CTransponder &transponder) { this->m_transponder = transponder; } + + //! Set transponder mode + void setTransponderMode(BlackMisc::Aviation::CTransponder::TransponderMode mode) { this->m_transponder.setTransponderMode(mode); } + + //! Set transponder code + void setTransponderCode(int code) { this->m_transponder.setTransponderCode(code); } + + //! Get transponder code + QString getTransponderCodeFormatted() const { return this->m_transponder.getTransponderCodeFormatted(); } + + //! Get transponder code + qint32 getTransponderCode() const { return this->m_transponder.getTransponderCode(); } + + //! Get transponder mode + BlackMisc::Aviation::CTransponder::TransponderMode getTransponderMode() const { return this->m_transponder.getTransponderMode(); } + + //! Is valid for login? + bool isValidForLogin() const; + + //! Meaningful default settings for COM Systems + void initComSystems(); + + //! Meaningful default settings for Transponder + void initTransponder(); + + //! Get aircraft parts + const BlackMisc::Aviation::CAircraftParts &getParts() const { return m_parts; } + + //! Get aircraft parts + BlackMisc::Aviation::CAircraftLights getLights() const; + + //! Set aircraft parts + void setParts(const BlackMisc::Aviation::CAircraftParts &parts); + + //! Set aircraft lights + void setLights(BlackMisc::Aviation::CAircraftLights &lights); + + //! Set aircraft lights on + void setAllLightsOn(); + + //! Set aircraft lights off + void setAllLightsOff(); + + //! VTOL aircraft? + bool isVtol() const; + + //! Combined ICAO / color string + QString getCombinedIcaoLiveryString() const; //! \copydoc CValueObject::propertyByIndex CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; @@ -63,23 +313,11 @@ namespace BlackMisc //! Set model string void setModelString(const QString &modelString); - //! \copydoc CAircraft::setCallsign - virtual void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) override; + //! Set callsign + void setCallsign(const BlackMisc::Aviation::CCallsign &callsign); - //! \copydoc CAircraft::setIcaoInfo - virtual void setIcaoInfo(const BlackMisc::Aviation::CAircraftIcaoData &icao) override; - - //! \copydoc CAircraft::setLivery - virtual void setLivery(const BlackMisc::Aviation::CLivery &livery) override; - - //! \copydoc CAircraft::setPilot - virtual void setPilot(const BlackMisc::Network::CUser &user) override; - - //! Get client - const BlackMisc::Network::CClient &getClient() const { return m_client; } - - //! Set client - void setClient(const BlackMisc::Network::CClient &client); + //! Set pilot + void setPilot(const BlackMisc::Network::CUser &user); //! Enabled? bool isEnabled() const { return m_enabled; } @@ -99,9 +337,6 @@ namespace BlackMisc //! Rendered? void setRendered(bool rendered) { m_rendered = rendered; } - //! Update from aviation aircraft - void setAircraft(const BlackMisc::Aviation::CAircraft &aircraft); - //! Have parts been synchronized with a remote client? bool isPartsSynchronized() const { return m_partsSynchronized; } @@ -113,8 +348,16 @@ namespace BlackMisc private: BLACK_ENABLE_TUPLE_CONVERSION(CSimulatedAircraft) - BlackMisc::Simulation::CAircraftModel m_model; - BlackMisc::Network::CClient m_client; + BlackMisc::Aviation::CCallsign m_callsign; + BlackMisc::Network::CUser m_pilot; + BlackMisc::Aviation::CAircraftSituation m_situation; + BlackMisc::Aviation::CComSystem m_com1system; + BlackMisc::Aviation::CComSystem m_com2system; + BlackMisc::Aviation::CTransponder m_transponder; + BlackMisc::Aviation::CAircraftParts m_parts; + BlackMisc::Aviation::CSelcal m_selcal; + BlackMisc::Aviation::CLivery m_livery; + BlackMisc::Simulation::CAircraftModel m_model; bool m_enabled = true; //!< to be displayed in sim bool m_rendered = false; //!< really shown in simulator bool m_partsSynchronized = false; //!< sync.parts @@ -128,8 +371,17 @@ namespace BlackMisc } // namespace BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Simulation::CSimulatedAircraft, ( + attr(o.m_callsign), + attr(o.m_pilot), + attr(o.m_situation), + attr(o.m_com1system), + attr(o.m_com2system), + attr(o.m_transponder), + attr(o.m_parts), + attr(o.m_livery), + attr(o.m_distanceToOwnAircraft), + attr(o.m_bearingToOwnAircraft), attr(o.m_model), - attr(o.m_client), attr(o.m_enabled), attr(o.m_rendered), attr(o.m_partsSynchronized), diff --git a/src/blackmisc/simulation/simulatedaircraftlist.cpp b/src/blackmisc/simulation/simulatedaircraftlist.cpp index 7f172de6f..32b647578 100644 --- a/src/blackmisc/simulation/simulatedaircraftlist.cpp +++ b/src/blackmisc/simulation/simulatedaircraftlist.cpp @@ -21,7 +21,6 @@ namespace BlackMisc { namespace Simulation { - CSimulatedAircraftList::CSimulatedAircraftList() { } CSimulatedAircraftList::CSimulatedAircraftList(const CSequence &other) : @@ -59,6 +58,26 @@ namespace BlackMisc return csl; } + bool CSimulatedAircraftList::updateWithVatsimDataFileData(CSimulatedAircraft &aircraftToBeUpdated) const + { + if (this->isEmpty()) return false; + if (aircraftToBeUpdated.hasValidRealName() && aircraftToBeUpdated.hasValidId() && aircraftToBeUpdated.hasAircraftAndAirlineDesignator()) { return false; } + + CSimulatedAircraft currentDataFileAircraft = this->findFirstByCallsign(aircraftToBeUpdated.getCallsign()); + if (currentDataFileAircraft.getCallsign().isEmpty()) return false; + + CUser user = aircraftToBeUpdated.getPilot(); + user.updateMissingParts(currentDataFileAircraft.getPilot()); + aircraftToBeUpdated.setPilot(user); + + CAircraftIcaoCode aircraftIcao = aircraftToBeUpdated.getAircraftIcaoCode(); + CAirlineIcaoCode airlineIcao = aircraftToBeUpdated.getAirlineIcaoCode(); + aircraftIcao.updateMissingParts(currentDataFileAircraft.getAircraftIcaoCode()); + airlineIcao.updateMissingParts(currentDataFileAircraft.getAirlineIcaoCode()); + aircraftToBeUpdated.setIcaoCodes(aircraftIcao, airlineIcao); + return true; + } + void CSimulatedAircraftList::markAllAsNotRendered() { for (CSimulatedAircraft &aircraft : (*this)) @@ -80,6 +99,18 @@ namespace BlackMisc return c; } + int CSimulatedAircraftList::setAircraftModel(const CCallsign &callsign, const CAircraftModel &model) + { + int c = 0; + for (CSimulatedAircraft &aircraft : (*this)) + { + if (aircraft.getCallsign() != callsign) { continue; } + aircraft.setModel(model); + c++; + } + return c; + } + int CSimulatedAircraftList::setAircraftParts(const CCallsign &callsign, const CAircraftParts &parts) { int c = 0; @@ -113,15 +144,5 @@ namespace BlackMisc return false; } - CAircraftList CSimulatedAircraftList::toAircraftList() const - { - CAircraftList al; - for (const CSimulatedAircraft &aircraft : (*this)) - { - al.push_back(aircraft); - } - return al; - } - } // namespace } // namespace diff --git a/src/blackmisc/simulation/simulatedaircraftlist.h b/src/blackmisc/simulation/simulatedaircraftlist.h index 904fcf360..77e8e5c8a 100644 --- a/src/blackmisc/simulation/simulatedaircraftlist.h +++ b/src/blackmisc/simulation/simulatedaircraftlist.h @@ -15,7 +15,6 @@ #include "blackmisc/blackmiscexport.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/aviation/callsignobjectlist.h" -#include "blackmisc/aviation/aircraftlist.h" #include "blackmisc/geo/geoobjectlist.h" #include "blackmisc/network/userlist.h" #include "blackmisc/collection.h" @@ -59,12 +58,19 @@ namespace BlackMisc //! Callsigns of aircraft with synchronized parts BlackMisc::Aviation::CCallsignSet getCallsignsWithSyncronizedParts() const; + //! Update aircraft with data from VATSIM data file + //! \remarks The list used ("this") needs to contain the VATSIM data file objects + bool updateWithVatsimDataFileData(CSimulatedAircraft &aircraftToBeUpdated) const; + //! Mark all aircraft as unrendered void markAllAsNotRendered(); //! Mark given callsign as rendered int setRendered(const BlackMisc::Aviation::CCallsign &callsign, bool rendered); + //! Set model + int setAircraftModel(const BlackMisc::Aviation::CCallsign &callsign, const CAircraftModel &model); + //! Set aircraft parts int setAircraftParts(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftParts &parts); @@ -74,9 +80,6 @@ namespace BlackMisc //! Rendered? bool isRendered(const BlackMisc::Aviation::CCallsign &callsign) const; - //! To aircraft list - BlackMisc::Aviation::CAircraftList toAircraftList() const; - }; } //namespace diff --git a/src/blackmisc/timestampbased.cpp b/src/blackmisc/timestampbased.cpp index d00ef29a6..c19956662 100644 --- a/src/blackmisc/timestampbased.cpp +++ b/src/blackmisc/timestampbased.cpp @@ -12,6 +12,14 @@ namespace BlackMisc { + ITimestampBased::ITimestampBased() + { } + + ITimestampBased::ITimestampBased(qint64 msSincePoch) : m_timestampMSecsSinceEpoch(msSincePoch) + { } + + ITimestampBased::ITimestampBased(const QDateTime ×tamp) : m_timestampMSecsSinceEpoch(timestamp.toMSecsSinceEpoch()) + { } QDateTime ITimestampBased::getUtcTimestamp() const { @@ -70,7 +78,7 @@ namespace BlackMisc this->m_timestampMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); } - QString ITimestampBased::getFormattedUtcTimestamp() const + QString ITimestampBased::getFormattedUtcTimestampDhms() const { return this->getUtcTimestamp().toString("dd hh:mm:ss"); } @@ -87,7 +95,7 @@ namespace BlackMisc QString ITimestampBased::getFormattedUtcTimestampYmdhms() const { - return this->getUtcTimestamp().toString("yyyy-MM-dd HH:mm::ss"); + return this->getUtcTimestamp().toString("yyyy-MM-dd HH:mm:ss"); } QString ITimestampBased::getFormattedUtcTimestampYmdhmsz() const @@ -112,12 +120,16 @@ namespace BlackMisc return CVariant::fromValue(this->getUtcTimestamp()); case IndexMSecsSinceEpoch: return CVariant::fromValue(this->getMSecsSinceEpoch()); - case IndexUtcTimestampFormatted: - return CVariant::fromValue(this->getFormattedUtcTimestamp()); + case IndexUtcTimestampFormattedDhms: + return CVariant::fromValue(this->getFormattedUtcTimestampDhms()); case IndexUtcTimestampFormattedHm: return CVariant::fromValue(this->getFormattedUtcTimestampHm()); case IndexUtcTimestampFormattedHms: return CVariant::fromValue(this->getFormattedUtcTimestampHms()); + case IndexUtcTimestampFormattedYmdhms: + return CVariant::fromValue(this->getFormattedUtcTimestampYmdhms()); + case IndexUtcTimestampFormattedYmdhmsz: + return CVariant::fromValue(this->getFormattedUtcTimestampYmdhmsz()); default: break; } @@ -140,7 +152,15 @@ namespace BlackMisc case IndexMSecsSinceEpoch: this->setMSecsSinceEpoch(variant.toInt()); return; - case IndexUtcTimestampFormatted: + case IndexUtcTimestampFormattedYmdhms: + case IndexUtcTimestampFormattedYmdhmsz: + case IndexUtcTimestampFormattedHm: + case IndexUtcTimestampFormattedHms: + case IndexUtcTimestampFormattedDhms: + { + QDateTime dt = QDateTime::fromString(variant.toQString()); + this->m_timestampMSecsSinceEpoch = dt.toMSecsSinceEpoch(); + } default: break; } diff --git a/src/blackmisc/timestampbased.h b/src/blackmisc/timestampbased.h index 4c5ed61ab..05bdc68a7 100644 --- a/src/blackmisc/timestampbased.h +++ b/src/blackmisc/timestampbased.h @@ -27,7 +27,9 @@ namespace BlackMisc enum ColumnIndex { IndexUtcTimestamp = BlackMisc::CPropertyIndex::GlobalIndexTimestampBased, - IndexUtcTimestampFormatted, + IndexUtcTimestampFormattedYmdhms, + IndexUtcTimestampFormattedYmdhmsz, + IndexUtcTimestampFormattedDhms, IndexUtcTimestampFormattedHms, IndexUtcTimestampFormattedHm, IndexMSecsSinceEpoch // keep this as last item @@ -73,7 +75,7 @@ namespace BlackMisc void setCurrentUtcTime(); //! Formatted timestamp - QString getFormattedUtcTimestamp() const; + QString getFormattedUtcTimestampDhms() const; //! As hh:mm:ss QString getFormattedUtcTimestampHms() const; @@ -91,14 +93,20 @@ namespace BlackMisc static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); protected: - //! Destructor - virtual ~ITimestampBased() {} + //! Constructor + ITimestampBased(); + + //! Constructor + ITimestampBased(qint64 msSincePoch); + + //! Constructor + ITimestampBased(const QDateTime ×tamp); //! \copydoc CValueObject::propertyByIndex - virtual CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; //! \copydoc CValueObject::setPropertyByIndex - virtual void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); + void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); qint64 m_timestampMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); //!< timestamp value }; diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index 242e89cdb..81062a763 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -12,11 +12,14 @@ #include "blackmisc/aviation/aircraftsituationlist.h" #include "blackmisc/aviation/aircraftpartslist.h" #include "blackmisc/aviation/liverylist.h" +#include "blackmisc/aviation/aircrafticaocodelist.h" +#include "blackmisc/aviation/airlineicaocodelist.h" #include "blackmisc/network/textmessagelist.h" #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/statusmessagelist.h" #include "blackmisc/identifierlist.h" +#include "blackmisc/countrylist.h" #include #include @@ -175,6 +178,8 @@ namespace BlackMisc template class ITimestampObjectList; template class ITimestampObjectList; template class ITimestampObjectList; + template class ITimestampObjectList; + template class ITimestampObjectList; template class ITimestampObjectList; template class ITimestampObjectList; @@ -183,5 +188,6 @@ namespace BlackMisc template class ITimestampObjectList; template class ITimestampObjectList; + template class ITimestampObjectList; } // namespace diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 5750f63b4..f799fc517 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -92,6 +92,10 @@ namespace BlackMisc class CAircraftPartsList; class CLivery; class CLiveryList; + class CAircraftIcaoCode; + class CAircraftIcaoCodeList; + class CAirlineIcaoCode; + class CAirlineIcaoCodeList; } namespace Network @@ -112,10 +116,14 @@ namespace BlackMisc class CStatusMessageList; class CIdentifier; class CIdentifierList; + class CCountry; + class CCountryList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; @@ -124,6 +132,7 @@ namespace BlackMisc extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE ITimestampObjectList; //! \endcond } //namespace