From e001b0338f8e83057e7eac4d6e174db6c83f2108 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 7 Feb 2018 05:21:41 +0100 Subject: [PATCH] Ref T242, utility functions for matching * search among airline aircraft * improved logging information * search among airline ICAO aircraft (which aircraft does the airline use?) * check ecosystem before merging with VATSIM data --- src/blackcore/aircraftmatcher.cpp | 59 +++++++++++++++++-- src/blackcore/aircraftmatcher.h | 24 +++++++- src/blackcore/airspacemonitor.cpp | 25 +++++++- src/blackcore/db/modeldatareader.cpp | 18 +++++- src/blackcore/db/modeldatareader.h | 11 +++- src/blackcore/webdataservices.cpp | 18 +++++- src/blackcore/webdataservices.h | 10 +++- src/blackmisc/aviation/airlineicaocode.cpp | 17 ++++-- src/blackmisc/aviation/airlineicaocode.h | 3 + src/blackmisc/simulation/aircraftmodel.cpp | 10 ++-- .../simulation/aircraftmodellist.cpp | 33 ++++++++++- src/blackmisc/simulation/aircraftmodellist.h | 18 +++++- src/blackmisc/stringutils.cpp | 9 +++ src/blackmisc/stringutils.h | 3 + 14 files changed, 231 insertions(+), 27 deletions(-) diff --git a/src/blackcore/aircraftmatcher.cpp b/src/blackcore/aircraftmatcher.cpp index e17bf3de6..39cfe16a8 100644 --- a/src/blackcore/aircraftmatcher.cpp +++ b/src/blackcore/aircraftmatcher.cpp @@ -69,7 +69,7 @@ namespace BlackCore break; } - CMatchingUtils::addLogDetailsToList(log, callsign, QString("Two invalid airline ICAO codes '%1', '%2'").arg(primaryIcao, secondaryIcao), getLogCategories()); + CMatchingUtils::addLogDetailsToList(log, callsign, QString("Two invalid airline ICAO codes (primary/secondary) '%1', '%2'").arg(primaryIcao, secondaryIcao), getLogCategories()); if (airlineFromCallsign) { const QString airlineSuffix = callsign.getAirlineSuffix(); @@ -94,8 +94,8 @@ namespace BlackCore static const QString format("hh:mm:ss.zzz"); const QDateTime startTime = QDateTime::currentDateTimeUtc(); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("--- Start matching: UTC %1 ---").arg(startTime.toString(format))); - CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("Matching uses model set of %1 models\n%2").arg(modelSet.size()).arg(modelSet.coverageSummary())); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("Input model: '%1' '%2'").arg(remoteAircraft.getCallsignAsString(), remoteAircraft.getModel().toQString())); + CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("Matching uses model set of %1 models\n%2").arg(modelSet.size()).arg(modelSet.extCoverageSummary(remoteAircraft.getModel()))); // Before I really search I check some special conditions // 1) Manually set model (by user) @@ -150,8 +150,8 @@ namespace BlackCore Q_ASSERT_X(matchedModel.hasModelString(), Q_FUNC_INFO, "Missing model string"); Q_ASSERT_X(matchedModel.getModelType() != CAircraftModel::TypeUnknown, Q_FUNC_INFO, "Missing model type"); - QDateTime endTime = QDateTime::currentDateTimeUtc(); - qint64 matchingTime = startTime.msecsTo(endTime); + const QDateTime endTime = QDateTime::currentDateTimeUtc(); + const qint64 matchingTime = startTime.msecsTo(endTime); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("--- Matching end: UTC %1, time %2ms ---").arg(endTime.toString(format)).arg(matchingTime)); return matchedModel; } @@ -411,6 +411,7 @@ namespace BlackCore { Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services"); + if (candidate.isEmpty()) { return ""; } const QStringList designators = sApp->getWebDataServices()->getTelephonyDesignators(); if (designators.contains(candidate, Qt::CaseInsensitive)) @@ -423,6 +424,56 @@ namespace BlackCore return ""; } + bool CAircraftMatcher::isKnowAircraftDesignator(const QString &candidate, const CCallsign &callsign, CStatusMessageList *log) + { + if (!CAircraftIcaoCode::isValidDesignator(candidate)) + { + CMatchingUtils::addLogDetailsToList(log, callsign, QString("No valid ICAO designator '%1'").arg(candidate)); + return false; + } + + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); + Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services"); + + const bool known = sApp->getWebDataServices()->containsAircraftIcaoDesignator(candidate); + static const QString sKnown("Known ICAO '%1'"); + static const QString sUnknown("Unknown ICAO '%1'"); + CMatchingUtils::addLogDetailsToList(log, callsign, known ? sKnown.arg(candidate) : sUnknown.arg(candidate)); + return known; + } + + CAircraftIcaoCode CAircraftMatcher::searchAmongAirlineAircraft(const QString &candidateString, const CAirlineIcaoCode &airline, const CCallsign &callsign, CStatusMessageList *log) + { + if (!airline.isLoadedFromDb()) + { + CMatchingUtils::addLogDetailsToList(log, callsign, QString("No valid airline from DB '%1'").arg(airline.getDesignator())); + return CAircraftIcaoCode(); + } + + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); + Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services"); + + const CAircraftIcaoCodeList aircraft = sApp->getWebDataServices()->getAircraftIcaoCodesForAirline(airline); + if (aircraft.isEmpty()) + { + CMatchingUtils::addLogDetailsToList(log, callsign, QString("No aircraft known for airline '%1'").arg(airline.getDesignator())); + return CAircraftIcaoCode(); + } + + const QSet allIcaos = aircraft.allIcaoCodes(); + const QString allIcaosStr = allIcaos.toList().join(", "); + CMatchingUtils::addLogDetailsToList(log, callsign, QString("Aircraft '%1' known for airline '%2'").arg(allIcaosStr, airline.getDesignator())); + + const CAircraftIcaoCode code = aircraft.findBestFuzzyMatchOrDefault(candidateString); + if (code.hasValidDesignator()) + { + CMatchingUtils::addLogDetailsToList(log, callsign, QString("Aircraft '%1' is best fuzzy search of '%2' for airline '%3'").arg(code.toQString(), candidateString, airline.getDesignator())); + return code; + } + + return aircraft.front(); + } + CAirlineIcaoCode CAircraftMatcher::callsignToAirline(const CCallsign &callsign, CStatusMessageList *log) { if (callsign.isEmpty() || !sApp || !sApp->getWebDataServices()) { return CAirlineIcaoCode(); } diff --git a/src/blackcore/aircraftmatcher.h b/src/blackcore/aircraftmatcher.h index 12c8dbecd..b2183b9a4 100644 --- a/src/blackcore/aircraftmatcher.h +++ b/src/blackcore/aircraftmatcher.h @@ -73,7 +73,7 @@ namespace BlackCore const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft, BlackMisc::CStatusMessageList *log = nullptr) const; - //! Return the airline ICAO being valid + //! Return an valid airline ICAO code //! \threadsafe static BlackMisc::Aviation::CAirlineIcaoCode failoverValidAirlineIcaoDesignator( const BlackMisc::Aviation::CCallsign &callsign, @@ -82,6 +82,7 @@ namespace BlackCore //! Try to find the corresponding data in DB and get best information for given data //! \threadsafe + //! \ingroup reverselookup static BlackMisc::Simulation::CAircraftModel reverseLookupModel( const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftIcaoCode &networkAircraftIcao, @@ -91,12 +92,14 @@ namespace BlackCore //! Try to find the corresponding data in DB and get best information for following matching //! \threadsafe + //! \ingroup reverselookup static BlackMisc::Simulation::CAircraftModel reverseLookupModel( const BlackMisc::Simulation::CAircraftModel &modelToLookup, const QString &networkLiveryInfo, BlackMisc::CStatusMessageList *log = nullptr); //! Try to find the DB corresponding ICAO code //! \threadsafe + //! \ingroup reverselookup static BlackMisc::Aviation::CAircraftIcaoCode reverseLookupAircraftIcao( const BlackMisc::Aviation::CAircraftIcaoCode &icaoDesignator, const BlackMisc::Aviation::CCallsign &logCallsign = BlackMisc::Aviation::CCallsign(), @@ -104,27 +107,46 @@ namespace BlackCore //! Try to find the DB corresponding ICAO code //! \threadsafe + //! \ingroup reverselookup static BlackMisc::Aviation::CAirlineIcaoCode reverseLookupAirlineIcao( const BlackMisc::Aviation::CAirlineIcaoCode &icaoPattern, const BlackMisc::Aviation::CCallsign &callsign = BlackMisc::Aviation::CCallsign(), BlackMisc::CStatusMessageList *log = nullptr); //! Lookup of standard livery //! \threadsafe + //! \ingroup reverselookup static BlackMisc::Aviation::CLivery reverseLookupStandardLivery( const BlackMisc::Aviation::CAirlineIcaoCode &airline, const BlackMisc::Aviation::CCallsign &callsign, BlackMisc::CStatusMessageList *log = nullptr); //! Lookup of airline name //! \threadsafe + //! \ingroup reverselookup static QString reverseLookupAirlineName( const QString &candidate, const BlackMisc::Aviation::CCallsign &callsign = {}, BlackMisc::CStatusMessageList *log = nullptr); //! Lookup of telephony designator //! \threadsafe + //! \ingroup reverselookup static QString reverseLookupTelephonyDesignator( const QString &candidate, const BlackMisc::Aviation::CCallsign &callsign = {}, BlackMisc::CStatusMessageList *log = nullptr); + //! Is this designator known? + //! \threadsafe + static bool isKnowAircraftDesignator( + const QString &candidate, const BlackMisc::Aviation::CCallsign &callsign = {}, + BlackMisc::CStatusMessageList *log = nullptr); + + //! Search among the airline aircraft + //! \remark only works if an airline is know + //! \threadsafe + static BlackMisc::Aviation::CAircraftIcaoCode searchAmongAirlineAircraft( + const QString &icaoString, + const BlackMisc::Aviation::CAirlineIcaoCode &airline, + const BlackMisc::Aviation::CCallsign &callsign = {}, + BlackMisc::CStatusMessageList *log = nullptr); + //! Turn callsign into airline //! \threadsafe static BlackMisc::Aviation::CAirlineIcaoCode callsignToAirline(const BlackMisc::Aviation::CCallsign &callsign, BlackMisc::CStatusMessageList *log = nullptr); diff --git a/src/blackcore/airspacemonitor.cpp b/src/blackcore/airspacemonitor.cpp index 9b85ab283..b0b67e754 100644 --- a/src/blackcore/airspacemonitor.cpp +++ b/src/blackcore/airspacemonitor.cpp @@ -971,7 +971,17 @@ namespace BlackCore airlineIcao = CAircraftMatcher::reverseLookupAirlineIcao(airlineIcao, callsign, log); } - const CAircraftIcaoCode aircraftIcao(aircraftIcaoString); + CAircraftIcaoCode aircraftIcao(aircraftIcaoString); + const bool knownAircraftIcao = CAircraftMatcher::isKnowAircraftDesignator(aircraftIcaoString, callsign, log); + if (airlineIcao.isLoadedFromDb() && !knownAircraftIcao) + { + // we have no valid aircraft ICAO, so we do a fuzzy search among those + CAircraftIcaoCode foundIcao = CAircraftMatcher::searchAmongAirlineAircraft(aircraftIcaoString, airlineIcao, callsign, log); + if (foundIcao.isLoadedFromDb()) { aircraftIcao = foundIcao; } + } + + CMatchingUtils::addLogDetailsToList(log, callsign, QString("Quality of aircraft ICAO: %1").arg(aircraftIcao.toQString(true)), getLogCategories()); + CMatchingUtils::addLogDetailsToList(log, callsign, QString("Quality of airline ICAO: %1").arg(airlineIcao.toQString(true)), getLogCategories()); return CAircraftMatcher::reverseLookupModel(callsign, aircraftIcao, airlineIcao, livery, modelString, type, log); } @@ -979,10 +989,14 @@ namespace BlackCore { const CCallsign callsign = aircraft.getCallsign(); Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign"); + if (this->isAircraftInRange(callsign)) { return false; } + if (!sApp || sApp->isShuttingDown()) { return false; } CSimulatedAircraft newAircraft(aircraft); newAircraft.calculcateAndUpdateRelativeDistanceAndBearing(this->getOwnAircraftPosition()); // distance from myself + + Q_ASSERT_X(sApp->hasWebDataServices(), Q_FUNC_INFO, "No web services"); sApp->getWebDataServices()->updateWithVatsimDataFileData(newAircraft); // store @@ -1345,9 +1359,16 @@ namespace BlackCore return m_network && m_network->isConnected(); } + const CServer CAirspaceMonitor::getConnectedServer() const + { + if (!this->isConnected()) { return CServer(); } + return m_network->getPresetServer(); + } + bool CAirspaceMonitor::supportsVatsimDataFile() const { - return sApp && sApp->getWebDataServices() && sApp->getWebDataServices()->getVatsimDataFileReader(); + const bool dataFile = sApp && sApp->getWebDataServices() && sApp->getWebDataServices()->getVatsimDataFileReader(); + return dataFile && this->getConnectedServer().getEcosystem().isSystem(CEcosystem::VATSIM); } CLength CAirspaceMonitor::calculateDistanceToOwnAircraft(const CAircraftSituation &situation) const diff --git a/src/blackcore/db/modeldatareader.cpp b/src/blackcore/db/modeldatareader.cpp index 0334b76d3..946004778 100644 --- a/src/blackcore/db/modeldatareader.cpp +++ b/src/blackcore/db/modeldatareader.cpp @@ -112,6 +112,20 @@ namespace BlackCore return models.findByKey(dbKey); } + QSet CModelDataReader::getAircraftDesignatorsForAirline(const CAirlineIcaoCode &code) const + { + if (!code.hasValidDesignator()) { return QSet(); } + const CAircraftModelList models(this->getModels()); + return models.getAircraftDesignatorsForAirline(code); + } + + CAircraftIcaoCodeList CModelDataReader::getAicraftIcaoCodesForAirline(const CAirlineIcaoCode &code) const + { + if (!code.hasValidDesignator()) { return CAircraftIcaoCodeList(); } + const CAircraftModelList models(this->getModels()); + return models.getAicraftIcaoCodesForAirline(code); + } + CAircraftModelList CModelDataReader::getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) { if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); } @@ -151,9 +165,9 @@ namespace BlackCore return this->getModels().toDbKeySet(); } - QStringList CModelDataReader::getModelStringList() const + QStringList CModelDataReader::getModelStringList(bool sort) const { - return this->getModels().getModelStringList(false); + return this->getModels().getModelStringList(sort); } bool CModelDataReader::areAllDataRead() const diff --git a/src/blackcore/db/modeldatareader.h b/src/blackcore/db/modeldatareader.h index 2c036d4fd..3db766db4 100644 --- a/src/blackcore/db/modeldatareader.h +++ b/src/blackcore/db/modeldatareader.h @@ -31,6 +31,7 @@ #include #include #include +#include class QNetworkReply; @@ -87,6 +88,14 @@ namespace BlackCore //! \threadsafe BlackMisc::Simulation::CAircraftModel getModelForDbKey(int dbKey) const; + //! Get aircraft ICAO designators (e.g. B737, ..) for given airline + //! \threadsafe + QSet getAircraftDesignatorsForAirline(const BlackMisc::Aviation::CAirlineIcaoCode &code) const; + + //! Get aircraft ICAO designators (e.g. B737, ..) for given airline + //! \threadsafe + BlackMisc::Aviation::CAircraftIcaoCodeList getAicraftIcaoCodesForAirline(const BlackMisc::Aviation::CAirlineIcaoCode &code) const; + //! Get model for designator/combined code //! \threadsafe BlackMisc::Simulation::CAircraftModelList getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode); @@ -117,7 +126,7 @@ namespace BlackCore //! Get model keys //! \threadsafe - QStringList getModelStringList() const; + QStringList getModelStringList(bool sort = false) const; //! All data read? //! \threadsafe diff --git a/src/blackcore/webdataservices.cpp b/src/blackcore/webdataservices.cpp index b7aba8042..6e65787a5 100644 --- a/src/blackcore/webdataservices.cpp +++ b/src/blackcore/webdataservices.cpp @@ -598,9 +598,9 @@ namespace BlackCore return QSet(); } - QStringList CWebDataServices::getModelStrings() const + QStringList CWebDataServices::getModelStrings(bool sort) const { - if (m_modelDataReader) { return m_modelDataReader->getModelStringList(); } + if (m_modelDataReader) { return m_modelDataReader->getModelStringList(sort); } return QStringList(); } @@ -646,6 +646,20 @@ namespace BlackCore return CAircraftIcaoCode(); } + QSet CWebDataServices::getAircraftDesignatorsForAirline(const CAirlineIcaoCode &airline) const + { + if (!airline.hasValidDesignator()) { return QSet(); } + if (m_modelDataReader) { return m_modelDataReader->getAircraftDesignatorsForAirline(airline); } + return QSet(); + } + + CAircraftIcaoCodeList CWebDataServices::getAircraftIcaoCodesForAirline(const CAirlineIcaoCode &airline) const + { + if (!airline.hasValidDesignator()) { return CAircraftIcaoCodeList(); } + if (m_modelDataReader) { return m_modelDataReader->getAicraftIcaoCodesForAirline(airline); } + return CAircraftIcaoCodeList(); + } + bool CWebDataServices::containsAircraftIcaoDesignator(const QString &designator) const { if (designator.isEmpty()) { return false; } diff --git a/src/blackcore/webdataservices.h b/src/blackcore/webdataservices.h index ee8822c68..28229c76b 100644 --- a/src/blackcore/webdataservices.h +++ b/src/blackcore/webdataservices.h @@ -216,7 +216,7 @@ namespace BlackCore //! Model strings //! \threadsafe - QStringList getModelStrings() const; + QStringList getModelStrings(bool sort = false) const; //! Model completer string //! \threadsafe @@ -246,6 +246,14 @@ namespace BlackCore //! \threadsafe BlackMisc::Aviation::CAircraftIcaoCode getAircraftIcaoCodeForDesignator(const QString &designator) const; + //! Aircraft ICAO designators for airline + //! \threadsafe + QSet getAircraftDesignatorsForAirline(const BlackMisc::Aviation::CAirlineIcaoCode &airline) const; + + //! Aircraft ICAO codes for airline + //! \threadsafe + BlackMisc::Aviation::CAircraftIcaoCodeList getAircraftIcaoCodesForAirline(const BlackMisc::Aviation::CAirlineIcaoCode &airline) const; + //! Contains the given designator? //! \threadsafe bool containsAircraftIcaoDesignator(const QString &designator) const; diff --git a/src/blackmisc/aviation/airlineicaocode.cpp b/src/blackmisc/aviation/airlineicaocode.cpp index d1be1be7e..a7e0c4e17 100644 --- a/src/blackmisc/aviation/airlineicaocode.cpp +++ b/src/blackmisc/aviation/airlineicaocode.cpp @@ -81,6 +81,13 @@ namespace BlackMisc } } + QString CAirlineIcaoCode::getDesignatorDbKey() const + { + return (this->isLoadedFromDb()) ? + this->getDesignator() % QStringLiteral(" ") % this->getDbKeyAsStringInParentheses() : + this->getDesignator(); + } + QString CAirlineIcaoCode::getDesignatorNameCountry() const { QString s(this->getDesignator()); @@ -178,11 +185,11 @@ namespace BlackMisc QString CAirlineIcaoCode::convertToQString(bool i18n) const { Q_UNUSED(i18n); - return m_designator % - QLatin1String(" (") % m_name % QLatin1String(")") % - QLatin1String(" Op: ") % boolToYesNo(this->isOperating()) % - QLatin1String(" VA: ") % boolToYesNo(this->isVirtualAirline()) % - QLatin1String(" Mil: ") % boolToYesNo(this->isMilitary()); + return this->getDesignatorDbKey() % + (this->hasName() ? QStringLiteral(" ") % m_name : QStringLiteral("")) % + QStringLiteral(" Op: ") % boolToYesNo(this->isOperating()) % + QStringLiteral(" VA: ") % boolToYesNo(this->isVirtualAirline()) % + QStringLiteral(" Mil: ") % boolToYesNo(this->isMilitary()); } CVariant CAirlineIcaoCode::propertyByIndex(const CPropertyIndex &index) const diff --git a/src/blackmisc/aviation/airlineicaocode.h b/src/blackmisc/aviation/airlineicaocode.h index 6e06c7029..82e9d59bd 100644 --- a/src/blackmisc/aviation/airlineicaocode.h +++ b/src/blackmisc/aviation/airlineicaocode.h @@ -90,6 +90,9 @@ namespace BlackMisc //! Get country, e.g. "FRANCE" const CCountry &getCountry() const { return m_country; } + //! Designator and DB key + QString getDesignatorDbKey() const; + //! Combined string designator, name, country QString getDesignatorNameCountry() const; diff --git a/src/blackmisc/simulation/aircraftmodel.cpp b/src/blackmisc/simulation/aircraftmodel.cpp index 2aceeb03d..7dbfba8d3 100644 --- a/src/blackmisc/simulation/aircraftmodel.cpp +++ b/src/blackmisc/simulation/aircraftmodel.cpp @@ -59,11 +59,11 @@ namespace BlackMisc QString CAircraftModel::convertToQString(bool i18n) const { const QString s = - m_modelString % - QLatin1String(" type: '") % this->getModelTypeAsString() % - QLatin1String("' ICAO: '") % this->getAircraftIcaoCode().toQString(i18n) % - QLatin1String("' {") % m_livery.toQString(i18n) % - QLatin1String("} file: '") % m_fileName % QLatin1String("'"); + (this->hasModelString() ? inApostrophes(m_modelString, true) % QStringLiteral(" ") : QStringLiteral("")) % + QStringLiteral(" type: '") % this->getModelTypeAsString() % + QStringLiteral("' ICAO: '") % this->getAircraftIcaoCode().toQString(i18n) % + QStringLiteral("' {") % m_livery.toQString(i18n) % + QStringLiteral("} file: '") % m_fileName % QStringLiteral("'"); return s; } diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index 9d2e629f7..0611c8558 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -82,6 +82,12 @@ namespace BlackMisc return this->contains(&CAircraftModel::getAircraftIcaoCodeDesignator, aircraftDesignator, &CAircraftModel::getAirlineIcaoCodeDesignator, airlineDesignator); } + bool CAircraftModelList::containsAirlineLivery(const CAirlineIcaoCode &airline) const + { + if (!airline.hasValidDesignator()) { return false; } + return this->contains(&CAircraftModel::getAirlineIcaoCode, airline); + } + CAircraftModelList CAircraftModelList::findByModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const { return this->findBy([ & ](const CAircraftModel & model) @@ -726,6 +732,18 @@ namespace BlackMisc return designators; } + CAircraftIcaoCodeList CAircraftModelList::getAicraftIcaoCodesForAirline(const CAirlineIcaoCode &airlineCode) const + { + CAircraftIcaoCodeList icaos; + if (!airlineCode.hasValidDesignator()) { return icaos; } + for (const CAircraftModel &model : *this) + { + if (model.getAirlineIcaoCode() != airlineCode) { continue; } + icaos.insert(model.getAircraftIcaoCode()); + } + return icaos; + } + QSet CAircraftModelList::getAirlineDesignators() const { QSet designators; @@ -807,7 +825,7 @@ namespace BlackMisc // normally prefer colors if there is no airline CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), QString("Prefer color liveries: '%1', airline: '%2', ignore zero scores: '%3'").arg(boolToYesNo(preferColorLiveries), remoteModel.getAirlineIcaoCodeDesignator(), boolToYesNo(ignoreZeroScores))); CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), QString("--- Start scoring in list with %1 models").arg(this->size())); - CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), this->coverageSummary()); + CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), this->extCoverageSummary(remoteModel)); int c = 1; for (const CAircraftModel &model : *this) @@ -1015,5 +1033,18 @@ namespace BlackMisc QStringLiteral("Different airlines: ") % QString::number(this->countDifferentAirlines()) % separator % QStringLiteral("Combined types: ") % this->getCombinedTypesAsString(); } + + QString CAircraftModelList::extCoverageSummary(const CAircraftModel &checkModel, const QString &separator) const + { + const bool combined = this->containsCombinedType(checkModel.getAircraftIcaoCode().getCombinedType()); + const bool airline = checkModel.hasAirlineDesignator() && this->containsAirlineLivery(checkModel.getAirlineIcaoCode()); + return coverageSummary(separator) % separator % + QStringLiteral("Has combined: ") % boolToYesNo(combined) % + ( + checkModel.hasAirlineDesignator() ? + QStringLiteral(" airline '") % checkModel.getAirlineIcaoCodeDesignator() % QStringLiteral("': ") % boolToYesNo(airline) : + QStringLiteral("") + ); + } } // namespace } // namespace diff --git a/src/blackmisc/simulation/aircraftmodellist.h b/src/blackmisc/simulation/aircraftmodellist.h index 76040b896..392bf96e4 100644 --- a/src/blackmisc/simulation/aircraftmodellist.h +++ b/src/blackmisc/simulation/aircraftmodellist.h @@ -15,9 +15,10 @@ #include "blackmisc/simulation/aircraftmodel.h" #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/simulatorinfo.h" +#include "blackmisc/aviation/aircrafticaocodelist.h" +#include "blackmisc/db/datastoreobjectlist.h" #include "blackmisc/blackmiscexport.h" #include "blackmisc/collection.h" -#include "blackmisc/db/datastoreobjectlist.h" #include "blackmisc/orderablelist.h" #include "blackmisc/sequence.h" #include "blackmisc/statusmessagelist.h" @@ -36,7 +37,7 @@ namespace BlackMisc { class CCallsign; class CLivery; - class CAirlineIcao; + class CAirlineIcaoCode; } namespace Simulation @@ -78,6 +79,9 @@ namespace BlackMisc //! Contains any model with aircraft and airline ICAO designator? bool containsModelsWithAircraftAndAirlineIcaoDesignator(const QString &aircraftDesignator, const QString &airlineDesignator) const; + //! Contains airline livery for given airline + bool containsAirlineLivery(const Aviation::CAirlineIcaoCode &airline) const; + //! Contains any model matching any of of passed simulators? bool containsMatchingSimulator(const CSimulatorInfo &simulators) const; @@ -275,6 +279,10 @@ namespace BlackMisc //! \remark gives all aircraft flown by an airline QSet getAircraftDesignatorsForAirline(const Aviation::CAirlineIcaoCode &airlineCode) const; + //! Aircraft ICAO codes for airline + //! \remark gives all aircraft flown by an airline + Aviation::CAircraftIcaoCodeList getAicraftIcaoCodesForAirline(const Aviation::CAirlineIcaoCode &airlineCode) const; + //! Airline designators QSet getAirlineDesignators() const; @@ -329,8 +337,12 @@ namespace BlackMisc //! As HTML summary QString asHtmlSummary() const; - //! What kind of models are represented here + //! What kind of models are represented here? QString coverageSummary(const QString &separator = "\n") const; + + //! What kind of models are represented here? + //! \remark checking for some criteria in the given model + QString extCoverageSummary(const CAircraftModel &checkModel, const QString &separator = "\n") const; }; } // ns } // ns diff --git a/src/blackmisc/stringutils.cpp b/src/blackmisc/stringutils.cpp index 9eecf6ce3..fab938dbb 100644 --- a/src/blackmisc/stringutils.cpp +++ b/src/blackmisc/stringutils.cpp @@ -12,6 +12,7 @@ #include "stringutils.h" #include #include +#include #include namespace BlackMisc @@ -322,6 +323,14 @@ namespace BlackMisc if (cs == Qt::CaseSensitive) { return c1 == c2; } return caseInsensitiveStringCompare(c1, c2); } + + const QString inApostrophes(const QString &in, bool ignoreEmpty) + { + static const QString e; + static const QString ea("''"); + if (in.isEmpty()) { return ignoreEmpty ? e : ea; } + return QStringLiteral("'") % in % QStringLiteral("'"); + } } //! \endcond diff --git a/src/blackmisc/stringutils.h b/src/blackmisc/stringutils.h index 34912706f..6e480e8f8 100644 --- a/src/blackmisc/stringutils.h +++ b/src/blackmisc/stringutils.h @@ -120,6 +120,9 @@ namespace BlackMisc return removeChars(string, [](QChar c) { return !c.isDigit(); }); } + //! Return string in apostrophes + BLACKMISC_EXPORT const QString inApostrophes(const QString &in, bool ignoreEmpty = false); + //! Bool to on/off BLACKMISC_EXPORT const QString &boolToOnOff(bool v);