diff --git a/samples/blackmiscsim/samplesmodelmapping.cpp b/samples/blackmiscsim/samplesmodelmapping.cpp index 21cc52ded..5e67e2757 100644 --- a/samples/blackmiscsim/samplesmodelmapping.cpp +++ b/samples/blackmiscsim/samplesmodelmapping.cpp @@ -67,7 +67,7 @@ namespace BlackSample // sync definitions, remove redundant ones CAircraftMatcher matcher(CAircraftMatcher::All); - matcher.setModelSet(vpRulesReader.getAsModelsFromCache()); + matcher.setModelSet(vpRulesReader.getAsModelsFromCache(), CSimulatorInfo::FSX); CAircraftIcaoCode icao("C172"); streamOut << "Searching for " << icao << endl; diff --git a/src/blackcore/aircraftmatcher.cpp b/src/blackcore/aircraftmatcher.cpp index 7c8d8a075..372aec44d 100644 --- a/src/blackcore/aircraftmatcher.cpp +++ b/src/blackcore/aircraftmatcher.cpp @@ -358,14 +358,14 @@ namespace BlackCore return icao; } - int CAircraftMatcher::setModelSet(const CAircraftModelList &models) + int CAircraftMatcher::setModelSet(const CAircraftModelList &models, const CSimulatorInfo &simulatorHint) { CAircraftModelList modelsCleaned(models); const int r1 = modelsCleaned.removeAllWithoutModelString(); const int r2 = modelsCleaned.removeIfExcluded(); if ((r1 + r2) > 0) { - CLogMessage(this).warning("Removed models for matcher, without string %1, excluded %2") << r1 << r2; + CLogMessage(this).warning("Removed models for matcher, without string '%1', excluded '%2'") << r1 << r2; } if (modelsCleaned.isEmpty()) { @@ -373,9 +373,11 @@ namespace BlackCore } else { - CLogMessage(this).info("Set %1 models in matcher") << modelsCleaned.size(); + CLogMessage(this).info("Set %1 models in matcher, simulator '%2'") << modelsCleaned.size() << simulatorHint.toQString(); } this->m_modelSet = modelsCleaned; + this->m_simulator = simulatorHint; + this->m_modelSetInfo = QString("Set: '%1' entries: %2").arg(simulatorHint.toQString()).arg(modelsCleaned.size()); return models.size(); } @@ -390,6 +392,58 @@ namespace BlackCore m_defaultModel.setModelType(CAircraftModel::TypeModelMatchingDefaultModel); } + CMatchingStatistics CAircraftMatcher::getCurrentStatistics() const + { + return this->m_statistics; + } + + void CAircraftMatcher::clearMatchingStatistics() + { + this->m_statistics.clear(); + } + + void CAircraftMatcher::evaluateStatisticsEntry(const QString &sessionId, const CCallsign &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery) + { + Q_UNUSED(livery); + Q_ASSERT_X(sApp && sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web data services"); + if (this->m_modelSet.isEmpty()) { return; } // ignore empty sets to not create silly stats + if (sessionId.isEmpty()) { return; } + if (aircraftIcao.isEmpty()) { return; } + + QString description; + if (!sApp->getWebDataServices()->getAircraftIcaoCodeForDesignator(aircraftIcao).isLoadedFromDb()) + { + description = QString("ICAO: '%1' not known, typo?").arg(aircraftIcao); + } + + // resolve airline, mostly needed because of vPilot not sending airline icao codes in version 1 + QString airlineIcaoChecked(airlineIcao.trimmed().toUpper()); + if (airlineIcao.isEmpty()) + { + const CAirlineIcaoCode al = CAircraftMatcher::reverseLookupAirlineIcao(airlineIcao, callsign); + if (al.isLoadedFromDb()) + { + airlineIcaoChecked = al.getDesignator(); + } + } + + CMatchingStatisticsEntry::EntryType type = CMatchingStatisticsEntry::Missing; + if (airlineIcaoChecked.isEmpty()) + { + type = this->m_modelSet.containsModelsWithAircraftAndAirlineIcaoDesignator(aircraftIcao, airlineIcao) ? + CMatchingStatisticsEntry::Found : + CMatchingStatisticsEntry::Missing; + } + else + { + type = this->m_modelSet.containsModelsWithAircraftAndAirlineIcaoDesignator(aircraftIcao, airlineIcao) ? + CMatchingStatisticsEntry::Found : + CMatchingStatisticsEntry::Missing; + + } + this->m_statistics.addAircraftAirlineCombination(type, sessionId, this->m_modelSetInfo, description, aircraftIcao, airlineIcao); + } + CAircraftModel CAircraftMatcher::getClosestMatchSearchImplementation(MatchingMode mode, const BlackMisc::Simulation::CAircraftModelList &modelSet, const CSimulatedAircraft &remoteAircraft, CStatusMessageList *log) const { BlackMisc::Simulation::CAircraftModelList matchedModels(modelSet); @@ -501,9 +555,8 @@ namespace BlackCore usedModelSet = modelSet; } - // first decide what set to use for scoring, it should not be too large - if (remoteAircraft.hasAircraftAndAirlineDesignator() && usedModelSet.containsModelsWithAircraftAndAirlineDesignator(remoteAircraft.getAircraftIcaoCodeDesignator(), remoteAircraft.getAirlineIcaoCodeDesignator())) + if (remoteAircraft.hasAircraftAndAirlineDesignator() && usedModelSet.containsModelsWithAircraftAndAirlineIcaoDesignator(remoteAircraft.getAircraftIcaoCodeDesignator(), remoteAircraft.getAirlineIcaoCodeDesignator())) { const CAircraftModelList byAircraftAndAirline(usedModelSet.findByIcaoDesignators(remoteAircraft.getAircraftIcaoCode(), remoteAircraft.getAirlineIcaoCode())); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QString("Using reduced set of %1 models by aircraft/airline ICAOs '%2'/'%3' for scoring").arg(byAircraftAndAirline.size()).arg(remoteAircraft.getAircraftIcaoCode().getDesignatorDbKey(), remoteAircraft.getAirlineIcaoCode().getVDesignatorDbKey()), getLogCategories()); diff --git a/src/blackcore/aircraftmatcher.h b/src/blackcore/aircraftmatcher.h index 907b2f07b..4685056b2 100644 --- a/src/blackcore/aircraftmatcher.h +++ b/src/blackcore/aircraftmatcher.h @@ -13,8 +13,8 @@ #define BLACKCORE_AIRCRAFTMATCHER_H #include "blackcore/blackcoreexport.h" -#include "blackmisc/simulation/aircraftmodel.h" #include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/simulation/matchingstatistics.h" #include "blackmisc/statusmessage.h" #include @@ -25,7 +25,7 @@ namespace BlackMisc { class CLogCategoryList; class CStatusMessageList; - namespace Aviation { class CCallsign; } + namespace Aviation { class CCallsign; } namespace Simulation { class CSimulatedAircraft; } } @@ -96,14 +96,18 @@ namespace BlackCore static BlackMisc::Aviation::CLivery reverseLookupStandardLivery(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); //! Get the models - BlackMisc::Simulation::CAircraftModelList getModelSet() const { return m_modelSet; } + const BlackMisc::Simulation::CAircraftModelList &getModelSet() const { return m_modelSet; } + + //! Models + bool hasModels() const { return !m_modelSet.isEmpty(); } //! Set the models we want to use - //! \note use a set from "somewhere else" so it can also be used with arbitrary sets for testing - int setModelSet(const BlackMisc::Simulation::CAircraftModelList &models); + //! \note uses a set from "somewhere else" so it can also be used with arbitrary sets for testing + int setModelSet(const BlackMisc::Simulation::CAircraftModelList &models, const BlackMisc::Simulation::CSimulatorInfo &simulatorHint = {}); //! Default model const BlackMisc::Simulation::CAircraftModel &getDefaultModel() const; @@ -111,6 +115,15 @@ namespace BlackCore //! Set default model void setDefaultModel(const BlackMisc::Simulation::CAircraftModel &defaultModel); + //! The current statistics + BlackMisc::Simulation::CMatchingStatistics getCurrentStatistics() const; + + //! Clear the statistics + void clearMatchingStatistics(); + + //! Evaluate if a statistics entry makes sense and add it + void evaluateStatisticsEntry(const QString &sessionId, const BlackMisc::Aviation::CCallsign &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery); + private: //! The search based implementation //! \threadsafe @@ -155,9 +168,12 @@ namespace BlackCore //! Scores to string for debugging static QString scoresToString(const BlackMisc::Simulation::ScoredModels &scores, int lastElements = 5); - MatchingMode m_matchingMode = All; - BlackMisc::Simulation::CAircraftModel m_defaultModel; //!< model to be used as default model - BlackMisc::Simulation::CAircraftModelList m_modelSet; //!< models used for model matching + MatchingMode m_matchingMode = All; + BlackMisc::Simulation::CAircraftModel m_defaultModel; //!< model to be used as default model + BlackMisc::Simulation::CAircraftModelList m_modelSet; //!< models used for model matching + BlackMisc::Simulation::CSimulatorInfo m_simulator; //!< simulator (optional) + BlackMisc::Simulation::CMatchingStatistics m_statistics; //!< matching statistics + QString m_modelSetInfo; }; } // namespace diff --git a/src/blackgui/components/modelmatchercomponent.cpp b/src/blackgui/components/modelmatchercomponent.cpp index eed692739..2a4fbfad4 100644 --- a/src/blackgui/components/modelmatchercomponent.cpp +++ b/src/blackgui/components/modelmatchercomponent.cpp @@ -95,7 +95,7 @@ namespace BlackGui { Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator"); this->m_modelSetLoader.changeSimulator(simulator); - this->m_matcher.setModelSet(this->m_modelSetLoader.getAircraftModels()); + this->m_matcher.setModelSet(this->m_modelSetLoader.getAircraftModels(), simulator); this->redisplay(); }