From f5942347f26670d98a1ae4f4879334144d784c97 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Mon, 4 Apr 2016 13:02:23 +0200 Subject: [PATCH] refs #614, improved model matcher --- src/blackmisc/simulation/aircraftmatcher.cpp | 258 +++++++++++++++---- src/blackmisc/simulation/aircraftmatcher.h | 47 ++-- 2 files changed, 223 insertions(+), 82 deletions(-) diff --git a/src/blackmisc/simulation/aircraftmatcher.cpp b/src/blackmisc/simulation/aircraftmatcher.cpp index e8e7a0ef2..3cb2e58a4 100644 --- a/src/blackmisc/simulation/aircraftmatcher.cpp +++ b/src/blackmisc/simulation/aircraftmatcher.cpp @@ -36,27 +36,15 @@ namespace BlackMisc CAircraftMatcher::~CAircraftMatcher() { } - void CAircraftMatcher::setModelMappingProvider(std::unique_ptr mappings) + CAircraftModel CAircraftMatcher::getClosestMatch(const CSimulatedAircraft &remoteAircraft, CStatusMessageList *log) const { - this->m_mappingsProvider = std::move(mappings); - CAircraftModelList models = this->m_mappingsProvider->getMatchingModels(); - int d = models.removeIfExcluded(); - this->m_models.uniqueWrite() = models; // local copy - CLogMessage(this).info("Added %1 models for model matching, %2 removed") << models.size() << d; - } + const CAircraftModelList matchModels(this->m_modelSet); // Models for this matching + if (matchModels.isEmpty()) + { + logDetails(log, remoteAircraft, "No models for matching, using default", CStatusMessage::SeverityWarning); + return this->getDefaultModel(); + } - void CAircraftMatcher::setLogDetails(bool log) - { - this->m_logDetails = log; - } - - void CAircraftMatcher::reload() - { - - } - - CAircraftModel CAircraftMatcher::getClosestMatch(const CSimulatedAircraft &remoteAircraft) const - { CAircraftModel aircraftModel(remoteAircraft.getModel()); // set defaults // Manually set string? @@ -64,44 +52,46 @@ namespace BlackMisc { // the user did a manual mapping "by hand", so he really should know what he is doing // no matching - this->logDetails(remoteAircraft, "Manually set model " + remoteAircraft.getModelString()); + logDetails(log, remoteAircraft, "Manually set model " + remoteAircraft.getModelString()); return remoteAircraft.getModel(); } - QString log; do { // try to find in installed models by model string - aircraftModel = matchByExactModelString(remoteAircraft); - if (aircraftModel.hasModelString()) - { - if (this->m_logDetails) { log = "Matched by exact model string " + aircraftModel.getModelString(); } - break; - } + aircraftModel = matchByExactModelString(remoteAircraft, matchModels, log); + if (aircraftModel.hasModelString()) { break; } - // ------------ start parts depending on swift DB data ------------------- + // by livery, then by ICAO + aircraftModel = matchByLiveryAndIcaoCode(remoteAircraft, matchModels, log); + if (aircraftModel.hasModelString()) { break; } - // by DB ICAO data - aircraftModel = matchModelsByIcaoData(remoteAircraft, log); - if (aircraftModel.hasModelString()) - { - break; - } + // by ICAO data from set + aircraftModel = matchModelsByIcaoData(remoteAircraft, matchModels, false, log); + if (aircraftModel.hasModelString()) { break; } - // ------------ end parts depending on swift DB data ------------------- + // family + QString family = remoteAircraft.getAircraftIcaoCode().getFamily(); + aircraftModel = matchByFamily(remoteAircraft, family, matchModels, "real family", log); + if (aircraftModel.hasModelString()) { break; } - aircraftModel = matchByFamily(remoteAircraft, log); + // scenario: the ICAO actually is the family + family = remoteAircraft.getAircraftIcaoCodeDesignator(); + aircraftModel = matchByFamily(remoteAircraft, family, matchModels, "ICAO treated as family", log); + if (aircraftModel.hasModelString()) { break; } + + // combined code + aircraftModel = matchByCombinedCode(remoteAircraft, matchModels, true, log); if (aircraftModel.hasModelString()) { break; } aircraftModel = getDefaultModel(); + logDetails(log, remoteAircraft, "Using default model " + aircraftModel.getModelString()); } while (false); // copy over callsign and other data aircraftModel.setCallsign(remoteAircraft.getCallsign()); - this->logDetails(aircraftModel, log); - Q_ASSERT_X(!aircraftModel.getCallsign().isEmpty(), Q_FUNC_INFO, "Missing callsign"); Q_ASSERT_X(aircraftModel.hasModelString(), Q_FUNC_INFO, "Missing model string"); Q_ASSERT_X(aircraftModel.getModelType() != CAircraftModel::TypeUnknown, Q_FUNC_INFO, "Missing model type"); @@ -109,6 +99,27 @@ namespace BlackMisc return aircraftModel; } + int CAircraftMatcher::setModelSet(const CAircraftModelList &models) + { + CAircraftModelList modelsCleaned(models); + int r1 = modelsCleaned.removeAllWithoutModelString(); + int r2 = modelsCleaned.removeIfExcluded(); + if ((r1 + r2) > 0) + { + CLogMessage(this).warning("Removed models for matcher, without string %1, excluded %2") << r1 << r2; + } + if (modelsCleaned.isEmpty()) + { + CLogMessage(this).error("No models for matching, that will not work"); + } + else + { + CLogMessage(this).info("Set %1 models in matcher") << modelsCleaned.size(); + } + this->m_modelSet = modelsCleaned; + return models.size(); + } + const CAircraftModel &CAircraftMatcher::getDefaultModel() const { return m_defaultModel; @@ -120,42 +131,177 @@ namespace BlackMisc m_defaultModel.setModelType(CAircraftModel::TypeModelMatchingDefaultModel); } - CAircraftModel CAircraftMatcher::matchByExactModelString(const CSimulatedAircraft &remoteAircraft) const + CAircraftModel CAircraftMatcher::matchByExactModelString(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &models, CStatusMessageList *log) { - const CAircraftModelList models(this->m_models.read()); - return models.findFirstByModelStringOrDefault(remoteAircraft.getModelString()); + if (remoteAircraft.getModelString().isEmpty()) + { + if (log) { logDetails(log, remoteAircraft, "No model string, no exact match possible"); } + return CAircraftModel(); + } + + CAircraftModel model = models.findFirstByModelStringOrDefault(remoteAircraft.getModelString()); + if (log) + { + if (model.hasModelString()) + { + this->logDetails(log, remoteAircraft, "Found exact match for " + model.getModelString()); + } + else + { + this->logDetails(log, remoteAircraft, "No exact match for " + model.getModelString()); + } + } + model.setModelType(CAircraftModel::TypeModelMatching); + return model; } - CAircraftModel CAircraftMatcher::matchModelsByIcaoData(const CSimulatedAircraft &remoteAircraft, QString &log) const + CAircraftModel CAircraftMatcher::matchModelsByIcaoData(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &models, bool ignoreAirline, CStatusMessageList *log) const { CAircraftModel aircraftModel; - const CAircraftModelList models(this->m_models.read()); + if (!remoteAircraft.hasAircraftDesignator()) + { + if (log) { this->logDetails(log, remoteAircraft, "No aircraft designator, skipping step"); } + return aircraftModel; + } + BlackMisc::Simulation::CAircraftModelList mappingModels( - models.findByIcaoDesignators(remoteAircraft.getAircraftIcaoCode(), remoteAircraft.getAirlineIcaoCode()) - ); + models.findByIcaoDesignators(remoteAircraft.getAircraftIcaoCode(), + ignoreAirline ? CAirlineIcaoCode() : remoteAircraft.getAirlineIcaoCode())); if (!mappingModels.isEmpty()) { - log = "Found by ICAO " + remoteAircraft.getAircraftIcaoCode().getDesignator() + " " + remoteAircraft.getAircraftIcaoCode().getDesignator(); - log = log.trimmed(); - CAircraftModel aircraftModel(mappingModels.front()); + aircraftModel = mappingModels.front(); aircraftModel.setModelType(CAircraftModel::TypeModelMatching); + if (log) + { + this->logDetails(log, remoteAircraft, + "Possible aircraft " + QString::number(mappingModels.size()) + + ", found by ICAO " + remoteAircraft.getAircraftIcaoCodeDesignator() + " " + + (ignoreAirline ? "" : remoteAircraft.getAirlineIcaoCodeDesignator())); + } + } + else + { + if (remoteAircraft.hasAircraftAndAirlineDesignator()) + { + // we have searched by aircraft and airline, bout not found anything + if (log) + { + this->logDetails(log, remoteAircraft, + "Not found by ICAO " + + remoteAircraft.getAircraftIcaoCodeDesignator() + " " + remoteAircraft.getAirlineIcaoCodeDesignator() + + " relaxing to only ICAO " + remoteAircraft.getAircraftIcaoCodeDesignator()); + } + // recursive lookup by ignoring airline + return this->matchModelsByIcaoData(remoteAircraft, models, true, log); + } + if (log) { this->logDetails(log, remoteAircraft, "Not found by ICAO " + remoteAircraft.getAircraftIcaoCodeDesignator() + " " + remoteAircraft.getAirlineIcaoCodeDesignator()); } } return aircraftModel; } - CAircraftModel CAircraftMatcher::matchByFamily(const CSimulatedAircraft &remoteAircraft, QString &log) const + CAircraftModel CAircraftMatcher::matchByFamily(const CSimulatedAircraft &remoteAircraft, const QString &family, const CAircraftModelList &models, const QString &modelSource, CStatusMessageList *log) const { // Use an algorithm to find the best match - Q_UNUSED(remoteAircraft); - Q_UNUSED(log); - return CAircraftModel(); + if (family.isEmpty()) + { + if (log) { this->logDetails(log, remoteAircraft, "No family, skipping step"); } + return CAircraftModel(); + } + if (models.isEmpty()) + { + if (log) { this->logDetails(log, remoteAircraft, "No models in " + modelSource + " for family match"); } + return CAircraftModel(); + } + + CAircraftModelList found(models.findByFamily(family)); + if (found.isEmpty()) + { + if (log) { this->logDetails(log, remoteAircraft, "Not found by family " + family + " in " + modelSource); } + return CAircraftModel(); + } + + if (remoteAircraft.hasAirlineDesignator()) + { + const CAircraftModelList foundReduceByAirline = found.findByIcaoDesignators(CAircraftIcaoCode(), remoteAircraft.getAirlineIcaoCode()); + if (!foundReduceByAirline.isEmpty()) + { + CAircraftModel model(found.front()); + if (log) + { + logDetails(log, + remoteAircraft, "Found by family " + family + " and airline " + + remoteAircraft.getAirlineIcaoCodeDesignator() + " (" + hint + ") as " + model.getAircraftIcaoCodeDesignator() + + " size " + QString::number(foundReduceByAirline.size())); + } + model.setModelType(CAircraftModel::TypeModelMatching); + return model; + } + } + + CAircraftModel model(found.front()); + logDetails(log, + remoteAircraft, "Found by family " + family + " (" + hint + ") " + + model.getAircraftIcaoCodeDesignator() + " size " + QString::number(found.size())); + model.setModelType(CAircraftModel::TypeModelMatching); + return model; } - void CAircraftMatcher::logDetails(const CSimulatedAircraft &remoteAircraft, const QString &message) const + CAircraftModelList CAircraftMatcher::ifPossibleReduceByManufacturer(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &inList, const QString &info, CStatusMessageList *log) { - if (!this->m_logDetails || message.isEmpty()) { return; } - const CCallsign callsign(remoteAircraft.getCallsign()); - CLogMessage(this).info(callsign.toQString(true) + ": " + message); + if (inList.isEmpty()) + { + if (log) { logDetails(log , remoteAircraft, info + " " + "Empty input list, cannot reduce"); } + return inList; + } + + const QString m = remoteAircraft.getAircraftIcaoCode().getManufacturer(); + if (m.isEmpty()) + { + if (log) { logDetails(log , remoteAircraft, info + " No manufacturer, cannot reduce " + QString::number(inList.size()) + " entries"); } + return inList; + } + + const CAircraftModelList outList(inList.findByManunfacturer(m)); + if (outList.isEmpty()) + { + if (log) { logDetails(log , remoteAircraft, info + " Not found " + m + ", cannot reduce"); } + return inList; + } + + if (log) { logDetails(log , remoteAircraft, info + " Reduced by " + m + " results: " + QString::number(outList.size())); } + return outList; + } + + CAircraftModelList CAircraftMatcher::ifPossibleReduceByAirline(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &inList, const QString &info, CStatusMessageList *log) + { + if (inList.isEmpty()) + { + if (log) { logDetails(log , remoteAircraft, info + " " + "Empty input list, cannot reduce"); } + return inList; + } + + if (!remoteAircraft.hasAirlineDesignator()) + { + if (log) { logDetails(log , remoteAircraft, info + " " + "No airline, cannot reduce " + QString::number(inList.size()) + " entries"); } + return inList; + } + + const CAircraftModelList outList(inList.findByIcaoDesignators(CAircraftIcaoCode(), remoteAircraft.getAirlineIcaoCode())); + if (outList.isEmpty()) + { + if (log) { logDetails(log , remoteAircraft, info + " Cannot reduce by " + remoteAircraft.getAirlineIcaoCodeDesignator() + " results: " + QString::number(outList.size())); } + } + + if (log) { logDetails(log , remoteAircraft, info + " Reduced reduce by " + remoteAircraft.getAirlineIcaoCodeDesignator() + " to " + QString::number(outList.size())); } + return outList; + } + + void CAircraftMatcher::logDetails(CStatusMessageList *log, const CSimulatedAircraft &remoteAircraft, const QString &message, CStatusMessage::StatusSeverity s) + { + if (!log) { return; } + if (message.isEmpty()) { return; } + const CStatusMessage m(getLogCategories(), s, remoteAircraft.hasCallsign() ? remoteAircraft.getCallsign().toQString() + ": " + message.trimmed() : message.trimmed()); + log->push_back(m); } } } // namespace diff --git a/src/blackmisc/simulation/aircraftmatcher.h b/src/blackmisc/simulation/aircraftmatcher.h index d8fe4bb35..e9ea92070 100644 --- a/src/blackmisc/simulation/aircraftmatcher.h +++ b/src/blackmisc/simulation/aircraftmatcher.h @@ -13,7 +13,6 @@ #define BLACKMISC_SIMULATION_AIRCRAFTMATCHER_H #include "blackmisc/blackmiscexport.h" -#include "blackmisc/simulation/modelmappingsprovider.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/simulation/aircraftmodellist.h" #include @@ -40,10 +39,10 @@ namespace BlackMisc //! Enabled matching mode flags enum MatchingModeFlag { - ExactMatch = 1 << 0, - ModelMapping = 1 << 1, - ModelMatching = 1 << 2, - AllModes = ExactMatch | ModelMapping | ModelMatching + ByModelString = 1 << 0, + ByIcaoData = 1 << 1, + ByFamily = 1 << 2, + All = ByModelString | ByIcaoData | ByFamily }; Q_DECLARE_FLAGS(MatchingMode, MatchingModeFlag) @@ -51,7 +50,7 @@ namespace BlackMisc static const BlackMisc::CLogCategoryList &getLogCategories(); //! Constructor - CAircraftMatcher(MatchingMode matchingMode = ModelMatching, QObject *parent = nullptr); + CAircraftMatcher(MatchingMode matchingMode = All, QObject *parent = nullptr); //! Destructor virtual ~CAircraftMatcher(); @@ -63,19 +62,15 @@ namespace BlackMisc //! Result depends on enabled modes. //! \sa MatchingModeFlag //! \threadsafe - CAircraftModel getClosestMatch(const CSimulatedAircraft &remoteAircraft) const; + CAircraftModel getClosestMatch(const CSimulatedAircraft &remoteAircraft, BlackMisc::CStatusMessageList *log = nullptr) const; - //! Get all mappings - BlackMisc::Simulation::CAircraftModelList getMatchingModels() const { return m_mappingsProvider->getMatchingModels(); } + //! Get the models + //! \threadsafe + BlackMisc::Simulation::CAircraftModelList getModels() const { return m_models.read(); } - //! Set the model mapping provider. The CAircraftMatcher will move the object and take over ownership - void setModelMappingProvider(std::unique_ptr mappings); - - //! Log.details? - void setLogDetails(bool log); - - //! Reload - void reload(); + //! Set the models we want to use + //! \threadsafe + int setModels(const BlackMisc::Simulation::CAircraftModelList &models); //! Default model const BlackMisc::Simulation::CAircraftModel &getDefaultModel() const; @@ -94,24 +89,24 @@ namespace BlackMisc //! Search in models by key (aka model string) //! \threadsafe - CAircraftModel matchByExactModelString(const CSimulatedAircraft &remoteAircraft) const; + CAircraftModel matchByExactModelString(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &models, CStatusMessageList *log) const; //! Installed models by ICAO data //! \threadsafe - CAircraftModel matchModelsByIcaoData(const CSimulatedAircraft &remoteAircraft, QString &log) const; + CAircraftModel matchModelsByIcaoData(const CSimulatedAircraft &remoteAircraft, const CAircraftModelList &models, bool ignoreAirline, CStatusMessageList *log) const; //! Find model by aircraft family - CAircraftModel matchByFamily(const CSimulatedAircraft &remoteAircraft, QString &log) const; + CAircraftModel matchByFamily(const CSimulatedAircraft &remoteAircraft, const QString &family, const CAircraftModelList &models, CStatusMessageList *log) const; - //! Log. details about mapping of particular aircraft - //! threadsafe - void logDetails(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft, const QString &message) const; + //! Add to log. if applicable + void logDetails(BlackMisc::CStatusMessageList *log, + const CSimulatedAircraft &remoteAircraft, + const QString &message, + CStatusMessage::StatusSeverity s = CStatusMessage::SeverityInfo) const; - MatchingMode m_matchingMode = ModelMatching; + MatchingMode m_matchingMode = All; BlackMisc::Simulation::CAircraftModel m_defaultModel; //!< model to be used as default model BlackMisc::LockFree m_models; //!< models used for model matching - std::unique_ptr m_mappingsProvider; //!< Provides all mapping definitions - std::atomic m_logDetails { false }; //!< log details }; } } // namespace