refs #706, callsign to airline plus testing resulting in multiple smaller improvements (refs #707)

* added functions to callsign / callsign list
* used in aircraft matcher
* also resolve std.livery in matcher
* also allow to find aircraft ICAO designator ending with string (e.g. 737 for B737)
* renamed CAircraftMatcher::reverseLookup -> CAircraftMatcher::reverselLookupModel
* threadsafe isInRange (CAIrspaceMonitor)
This commit is contained in:
Klaus Basan
2016-07-09 18:29:37 +02:00
parent 19040af654
commit 64cee7fba8
16 changed files with 196 additions and 47 deletions

View File

@@ -167,7 +167,7 @@ namespace BlackCore
return matchedModel; return matchedModel;
} }
CAircraftModel CAircraftMatcher::reverseLookup(const CAircraftModel &modelToLookup, const QString &networkLiveryInfo, CStatusMessageList *log) CAircraftModel CAircraftMatcher::reverselLookupModel(const CAircraftModel &modelToLookup, const QString &networkLiveryInfo, CStatusMessageList *log)
{ {
Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp");
Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services"); Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services");
@@ -279,8 +279,8 @@ namespace BlackCore
const CAircraftIcaoCode icao = sApp->getWebDataServices()->smartAircraftIcaoSelector(designator); const CAircraftIcaoCode icao = sApp->getWebDataServices()->smartAircraftIcaoSelector(designator);
if (log) if (log)
{ {
if (icao.hasValidDbKey()) { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup for ICAO '%1' found '%2'").arg(designator).arg(icao.getDesignator())); } if (icao.hasValidDbKey()) { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup for ICAO '%1' found '%2'").arg(designator).arg(icao.getDesignator()), CAircraftMatcher::getLogCategories()); }
else { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of ICAO '%1'', nothing found").arg(designator)); } else { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of ICAO '%1'', nothing found").arg(designator), CAircraftMatcher::getLogCategories()); }
} }
return icao; return icao;
} }
@@ -291,20 +291,40 @@ namespace BlackCore
Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services"); Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services");
const QString designator(icaoDesignator.trimmed().toUpper()); const QString designator(icaoDesignator.trimmed().toUpper());
const CAirlineIcaoCode icao = sApp->getWebDataServices()->smartAirlineIcaoSelector(designator); const CAirlineIcaoCode icao = sApp->getWebDataServices()->smartAirlineIcaoSelector(designator, callsign);
if (log) if (log)
{ {
if (icao.hasValidDbKey()) { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of airline '%1' found '%2'").arg(designator).arg(icao.getDesignator())); } if (icao.hasValidDbKey()) { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of airline '%1' and callsign '%2' found '%3'").arg(designator).arg(callsign.asString()).arg(icao.getDesignator()), CAircraftMatcher::getLogCategories()); }
else { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of airline '%1', nothing found").arg(designator)); } else { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of airline '%1' and callsign '%2', nothing found").arg(designator).arg(callsign.asString()), CAircraftMatcher::getLogCategories()); }
} }
return icao; return icao;
} }
CLivery CAircraftMatcher::reverseLookupStandardLivery(const CAirlineIcaoCode &airline, const CCallsign &callsign, CStatusMessageList *log)
{
Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp");
Q_ASSERT_X(sApp->getWebDataServices(), Q_FUNC_INFO, "No web services");
if (!airline.hasValidDesignator())
{
if (log) { CMatchingUtils::addLogDetailsToList(log, callsign, "Reverse lookup of standard livery skipped, no airline designator", CAircraftMatcher::getLogCategories(), CStatusMessage::SeverityWarning); }
return CLivery();
}
const CLivery livery = sApp->getWebDataServices()->getStdLiveryForAirlineCode(airline);
if (log)
{
if (livery.hasValidDbKey()) { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Reverse lookup of standard livery for '%1' found '%2'").arg(airline.getDesignator()).arg(livery.getCombinedCode()), CAircraftMatcher::getLogCategories()); }
else { CMatchingUtils::addLogDetailsToList(log, callsign, QString("Not standard livery for airline '%1'").arg(airline.getDesignator()), CAircraftMatcher::getLogCategories()); }
}
return livery;
}
int CAircraftMatcher::setModelSet(const CAircraftModelList &models) int CAircraftMatcher::setModelSet(const CAircraftModelList &models)
{ {
CAircraftModelList modelsCleaned(models); CAircraftModelList modelsCleaned(models);
int r1 = modelsCleaned.removeAllWithoutModelString(); const int r1 = modelsCleaned.removeAllWithoutModelString();
int r2 = modelsCleaned.removeIfExcluded(); const int r2 = modelsCleaned.removeIfExcluded();
if ((r1 + r2) > 0) 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;

View File

@@ -74,7 +74,7 @@ namespace BlackCore
//! Try to find the corresponding data in DB and get best information for following matching //! Try to find the corresponding data in DB and get best information for following matching
//! \threadsafe //! \threadsafe
static BlackMisc::Simulation::CAircraftModel reverseLookup(const BlackMisc::Simulation::CAircraftModel &modelToLookup, const QString &networkLiveryInfo, BlackMisc::CStatusMessageList *log = nullptr); static BlackMisc::Simulation::CAircraftModel reverselLookupModel(const BlackMisc::Simulation::CAircraftModel &modelToLookup, const QString &networkLiveryInfo, BlackMisc::CStatusMessageList *log = nullptr);
//! Try to find the DB corresponding ICAO code //! Try to find the DB corresponding ICAO code
//! \threadsafe //! \threadsafe
@@ -84,6 +84,10 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
static BlackMisc::Aviation::CAirlineIcaoCode reverseLookupAirlineIcao(const QString &icaoDesignator, const BlackMisc::Aviation::CCallsign &callsign = BlackMisc::Aviation::CCallsign(), BlackMisc::CStatusMessageList *log = nullptr); static BlackMisc::Aviation::CAirlineIcaoCode reverseLookupAirlineIcao(const QString &icaoDesignator, const BlackMisc::Aviation::CCallsign &callsign = BlackMisc::Aviation::CCallsign(), BlackMisc::CStatusMessageList *log = nullptr);
//! Lookup of standard livery
//! \threadsafe
static BlackMisc::Aviation::CLivery reverseLookupStandardLivery(const BlackMisc::Aviation::CAirlineIcaoCode &airline, const BlackMisc::Aviation::CCallsign &callsign, BlackMisc::CStatusMessageList *log = nullptr);
//! Get the models //! Get the models
BlackMisc::Simulation::CAircraftModelList getModelSet() const { return m_modelSet; } BlackMisc::Simulation::CAircraftModelList getModelSet() const { return m_modelSet; }

View File

@@ -376,6 +376,13 @@ namespace BlackCore
return this->getOtherClients().containsCallsign(callsign); return this->getOtherClients().containsCallsign(callsign);
} }
bool CAirspaceMonitor::isInRange(const CCallsign &callsign) const
{
if (callsign.isEmpty()) { return false; }
QReadLocker l(&m_lockAircraft);
return m_aircraftInRange.containsCallsign(callsign);
}
CClientList CAirspaceMonitor::getOtherClients() const CClientList CAirspaceMonitor::getOtherClients() const
{ {
QReadLocker l(&m_lockClient); QReadLocker l(&m_lockClient);
@@ -653,7 +660,7 @@ namespace BlackCore
// check if the name and ICAO query went properly through, those usually mean the aircraft are in range and can be used // check if the name and ICAO query went properly through, those usually mean the aircraft are in range and can be used
// this here is part of the reverse lookup process, where we turn FSD data into a model we assume the other user is flying // this here is part of the reverse lookup process, where we turn FSD data into a model we assume the other user is flying
const bool maxTrialsReached = trial >= 3; const bool maxTrialsReached = trial >= 2;
const bool inRange = remoteAircraft.hasValidCallsign(); // found? otherwise we need to wait for some data const bool inRange = remoteAircraft.hasValidCallsign(); // found? otherwise we need to wait for some data
bool validData = inRange && remoteAircraft.hasAircraftDesignator(); bool validData = inRange && remoteAircraft.hasAircraftDesignator();
if (!maxTrialsReached && validData && !remoteAircraft.getModel().hasModelString()) if (!maxTrialsReached && validData && !remoteAircraft.getModel().hasModelString())
@@ -852,7 +859,7 @@ namespace BlackCore
} }
else else
{ {
CPropertyIndexVariantMap vm(CClient::IndexModelString, modelString); const CPropertyIndexVariantMap vm(CClient::IndexModelString, modelString);
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm); // update client info this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm); // update client info
} }
} }
@@ -873,14 +880,18 @@ namespace BlackCore
void CAirspaceMonitor::icaoOrFsdDataReceived(const CCallsign &callsign, const QString &aircraftIcaoDesignator, const QString &airlineIcaoDesignator, const QString &livery, const QString &modelString, CAircraftModel::ModelType type) void CAirspaceMonitor::icaoOrFsdDataReceived(const CCallsign &callsign, const QString &aircraftIcaoDesignator, const QString &airlineIcaoDesignator, const QString &livery, const QString &modelString, CAircraftModel::ModelType type)
{ {
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "not in main thread"); Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "not in main thread");
Q_ASSERT_X(type == CAircraftModel::TypeFsdData || type == CAircraftModel::TypeQueriedFromNetwork, Q_FUNC_INFO, "Wrong type");
BLACK_VERIFY_X(callsign.isValid(), Q_FUNC_INFO, "invalid callsign"); BLACK_VERIFY_X(callsign.isValid(), Q_FUNC_INFO, "invalid callsign");
if (!callsign.isValid()) { return; } if (!callsign.isValid()) { return; }
if (!this->m_connected) { return; } if (!this->m_connected) { return; }
if (aircraftIcaoDesignator.isEmpty() && airlineIcaoDesignator.isEmpty() && livery.isEmpty()) { return; } if (aircraftIcaoDesignator.isEmpty() && airlineIcaoDesignator.isEmpty() && livery.isEmpty() && modelString.isEmpty()) { return; }
CStatusMessageList reverseLookupMessages; CStatusMessageList reverseLookupMessages;
CStatusMessageList *pReverseLookupMessages = this->isReverseLookupMessagesEnabled() ? &reverseLookupMessages : nullptr; CStatusMessageList *pReverseLookupMessages = this->isReverseLookupMessagesEnabled() ? &reverseLookupMessages : nullptr;
CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("Data from network: aircraft '%1', airline '%2', livery '%3', model '%4'").arg(aircraftIcaoDesignator).arg(airlineIcaoDesignator).arg(livery).arg(modelString)); CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("Data from network (%1): aircraft '%2', airline '%3', livery '%4', model '%5'").
arg(CAircraftModel::modelTypeToString(type)).
arg(aircraftIcaoDesignator).arg(airlineIcaoDesignator).
arg(livery).arg(modelString));
const CSimulatedAircraft remoteAircraft(this->getAircraftInRangeForCallsign(callsign)); const CSimulatedAircraft remoteAircraft(this->getAircraftInRangeForCallsign(callsign));
const bool existingAircraft = !remoteAircraft.getCallsign().isEmpty(); const bool existingAircraft = !remoteAircraft.getCallsign().isEmpty();
@@ -900,10 +911,10 @@ namespace BlackCore
// already matched with DB? Means we already have DB data in cache or existing model // already matched with DB? Means we already have DB data in cache or existing model
if (!model.canInitializeFromFsd()) { return; } if (!model.canInitializeFromFsd()) { return; }
// update model string if not yet existing // update type to FSD if applicable
if (model.getModelType() == CAircraftModel::TypeUnknown || model.getModelType() == CAircraftModel::TypeQueriedFromNetwork) if (model.getModelType() == CAircraftModel::TypeFsdData)
{ {
model.setModelType(type); // update type if no type yet model.setModelType(CAircraftModel::TypeFsdData); // update type if no type yet
} }
// //
@@ -931,23 +942,50 @@ namespace BlackCore
CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("No DB data for aircraft %1").arg(aircraftIcaoDesignator), getLogCategories()); CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("No DB data for aircraft %1").arg(aircraftIcaoDesignator), getLogCategories());
} }
} }
if (!model.hasAirlineDesignator() && !airlineIcaoDesignator.isEmpty())
// Derive airline from callsign
CAirlineIcaoCode airlineIcaoDesignatorReviewed(airlineIcaoDesignator);
if (type == CAircraftModel::TypeQueriedFromNetwork && airlineIcaoDesignator.isEmpty())
{ {
const CAirlineIcaoCode reverseIcao = CAircraftMatcher::reverseLookupAirlineIcao(airlineIcaoDesignator, callsign, pReverseLookupMessages); // try to conclude from callsign
CLivery defaultLivery = model.getLivery(); airlineIcaoDesignatorReviewed = CAirspaceMonitor::callsignToAirline(callsign);
if (reverseIcao.hasValidDbKey()) if (airlineIcaoDesignatorReviewed.hasValidDesignator())
{ {
defaultLivery.setAirlineIcaoCode(reverseIcao); CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("Turned callsign %1 into airline %2").arg(callsign.asString()).arg(airlineIcaoDesignatorReviewed.getDesignator()), getLogCategories());
} }
else else
{ {
defaultLivery.setAirlineIcaoCode(airlineIcaoDesignator); CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("Cannot turn callsign %1 into airline").arg(callsign.asString()), getLogCategories());
CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("No DB data for airline %1").arg(aircraftIcaoDesignator), getLogCategories()); }
}
// Set livery / airline
if (!model.getLivery().hasValidDbKey())
{
if (!model.hasAirlineDesignator() && airlineIcaoDesignatorReviewed.hasValidDesignator())
{
const CAirlineIcaoCode reverseIcao = CAircraftMatcher::reverseLookupAirlineIcao(airlineIcaoDesignatorReviewed.getDesignator(), callsign, pReverseLookupMessages);
CLivery defaultLivery = CAircraftMatcher::reverseLookupStandardLivery(reverseIcao, callsign, pReverseLookupMessages);
if (!defaultLivery.hasValidDbKey())
{
// No DB livery, create one
if (reverseIcao.hasValidDbKey())
{
defaultLivery.setAirlineIcaoCode(reverseIcao);
}
else
{
defaultLivery.setAirlineIcaoCode(airlineIcaoDesignatorReviewed);
CMatchingUtils::addLogDetailsToList(pReverseLookupMessages, callsign, QString("No DB data for airline %1").arg(aircraftIcaoDesignator), getLogCategories());
}
}
} }
} }
// now try resolution to model from DB // now try resolution to model from DB
model = CAircraftMatcher::reverseLookup(model, livery, &reverseLookupMessages); model = CAircraftMatcher::reverselLookupModel(model, livery, &reverseLookupMessages);
// messages
if (this->m_enableReverseLookupMsgs && !reverseLookupMessages.isEmpty()) if (this->m_enableReverseLookupMsgs && !reverseLookupMessages.isEmpty())
{ {
this->addReverseLookupMessages(callsign, reverseLookupMessages); this->addReverseLookupMessages(callsign, reverseLookupMessages);
@@ -1000,6 +1038,13 @@ namespace BlackCore
this->addReverseLookupMessage(callsign, m); this->addReverseLookupMessage(callsign, m);
} }
CAirlineIcaoCode CAirspaceMonitor::callsignToAirline(const CCallsign &callsign)
{
if (callsign.isEmpty() || !sApp || !sApp->getWebDataServices()) { return CAirlineIcaoCode(); }
const CAirlineIcaoCode icao = sApp->getWebDataServices()->findBestMatchByCallsign(callsign);
return icao;
}
void CAirspaceMonitor::ps_aircraftUpdateReceived(const CAircraftSituation &situation, const CTransponder &transponder) void CAirspaceMonitor::ps_aircraftUpdateReceived(const CAircraftSituation &situation, const CTransponder &transponder)
{ {
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread"); Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread");
@@ -1012,7 +1057,7 @@ namespace BlackCore
this->storeAircraftSituation(situation); this->storeAircraftSituation(situation);
emit this->addedAircraftSituation(situation); emit this->addedAircraftSituation(situation);
bool existsInRange = this->m_aircraftInRange.containsCallsign(callsign); const bool existsInRange = this->isInRange(callsign);
if (!existsInRange) if (!existsInRange)
{ {
// new aircraft // new aircraft
@@ -1048,7 +1093,7 @@ namespace BlackCore
QWriteLocker l(&m_lockClient); QWriteLocker l(&m_lockClient);
if (!this->m_otherClients.containsCallsign(callsign)) if (!this->m_otherClients.containsCallsign(callsign))
{ {
CClient c(callsign); const CClient c(callsign);
this->m_otherClients.push_back(c); // initial, will be filled by data later this->m_otherClients.push_back(c); // initial, will be filled by data later
} }
} }
@@ -1112,30 +1157,32 @@ namespace BlackCore
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread"); Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread");
if (!this->m_connected) { return; } if (!this->m_connected) { return; }
CCallsign callsign(situation.getCallsign()); const CCallsign callsign(situation.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign"); Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
// todo: Check if the timestamp is copied here as well.
// Interim packets do not have groundspeed, hence set the last known value. // Interim packets do not have groundspeed, hence set the last known value.
// If there is no full position available yet, throw this interim position away. // If there is no full position available yet, throw this interim position away.
CAircraftSituation interimSituation(situation); CAircraftSituation interimSituation(situation);
CAircraftSituationList history;
{ {
QReadLocker l(&m_lockSituations); QReadLocker l(&m_lockSituations);
auto history = this->m_situationsByCallsign[callsign]; history = this->m_situationsByCallsign[callsign];
if (history.empty()) { return; } // we need one full situation
interimSituation.setCurrentUtcTime();
interimSituation.setGroundSpeed(history.latestObject().getGroundSpeed());
} }
if (history.empty()) { return; } // we need one full situation at least
const CAircraftSituation lastSituation = history.latestObject();
if (lastSituation.getPosition() == interimSituation.getPosition()) { return; } // save position, ignore
// changed position, continue and copy values
interimSituation.setCurrentUtcTime();
interimSituation.setGroundSpeed(lastSituation.getGroundSpeed());
// store situation history // store situation history
this->storeAircraftSituation(interimSituation); this->storeAircraftSituation(interimSituation);
emit this->addedAircraftSituation(interimSituation); emit this->addedAircraftSituation(interimSituation);
// update aircraft // update aircraft
//! \todo skip aircraft updates for interim positions as for performance reasons
CLength distance = getOwnAircraft().calculateGreatCircleDistance(interimSituation.getPosition()); CLength distance = getOwnAircraft().calculateGreatCircleDistance(interimSituation.getPosition());
distance.switchUnit(CLengthUnit::NM()); // lloks nicer distance.switchUnit(CLengthUnit::NM()); // looks nicer
CPropertyIndexVariantMap vm; CPropertyIndexVariantMap vm;
vm.addValue(CSimulatedAircraft::IndexSituation, interimSituation); vm.addValue(CSimulatedAircraft::IndexSituation, interimSituation);
vm.addValue(CSimulatedAircraft::IndexDistanceToOwnAircraft, distance); vm.addValue(CSimulatedAircraft::IndexDistanceToOwnAircraft, distance);

View File

@@ -127,6 +127,10 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
bool hasClientInfo(const BlackMisc::Aviation::CCallsign &callsign) const; bool hasClientInfo(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Is aircraft in range?
//! \threadsafe
bool isInRange(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Returns the current online ATC stations //! Returns the current online ATC stations
BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const { return m_atcStationsOnline; } BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const { return m_atcStationsOnline; }
@@ -268,6 +272,9 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
void addReverseLookupMessage(const BlackMisc::Aviation::CCallsign &callsign, const QString &message, BlackMisc::CStatusMessage::StatusSeverity severity = BlackMisc::CStatusMessage::SeverityInfo); void addReverseLookupMessage(const BlackMisc::Aviation::CCallsign &callsign, const QString &message, BlackMisc::CStatusMessage::StatusSeverity severity = BlackMisc::CStatusMessage::SeverityInfo);
//! Turn callsign into airline
static BlackMisc::Aviation::CAirlineIcaoCode callsignToAirline(const BlackMisc::Aviation::CCallsign &callsign);
private slots: private slots:
//! Create aircraft in range, this is the only place where a new aircraft should be added //! Create aircraft in range, this is the only place where a new aircraft should be added
void ps_aircraftUpdateReceived(const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder); void ps_aircraftUpdateReceived(const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);

View File

@@ -24,7 +24,6 @@ namespace BlackCore
class BLACKCORE_EXPORT CMatchingUtils class BLACKCORE_EXPORT CMatchingUtils
{ {
public: public:
//! Specialized log for matching / reverse lookup //! Specialized log for matching / reverse lookup
//! \threadsafe //! \threadsafe
static void addLogDetailsToList( static void addLogDetailsToList(

View File

@@ -351,6 +351,13 @@ namespace BlackCore
return CAirlineIcaoCode(); return CAirlineIcaoCode();
} }
CAirlineIcaoCode CWebDataServices::findBestMatchByCallsign(const CCallsign &callsign) const
{
if (callsign.isEmpty()) { return CAirlineIcaoCode(); }
const CAirlineIcaoCodeList icaos(this->getAirlineIcaoCodes());
return icaos.findBestMatchByCallsign(callsign);
}
CAirlineIcaoCode CWebDataServices::getAirlineIcaoCodeForDbKey(int key) const CAirlineIcaoCode CWebDataServices::getAirlineIcaoCodeForDbKey(int key) const
{ {
if (m_icaoDataReader) { return m_icaoDataReader->getAirlineIcaoCodeForDbKey(key); } if (m_icaoDataReader) { return m_icaoDataReader->getAirlineIcaoCodeForDbKey(key); }

View File

@@ -246,6 +246,10 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
BlackMisc::Aviation::CAirlineIcaoCode smartAirlineIcaoSelector(const BlackMisc::Aviation::CAirlineIcaoCode &code) const; BlackMisc::Aviation::CAirlineIcaoCode smartAirlineIcaoSelector(const BlackMisc::Aviation::CAirlineIcaoCode &code) const;
//! ICAO code for callsign (e.g. DLH123 -> DLH)
//! \threadsafe
BlackMisc::Aviation::CAirlineIcaoCode findBestMatchByCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Countries //! Countries
//! \threadsafe //! \threadsafe
BlackMisc::CCountryList getCountries() const; BlackMisc::CCountryList getCountries() const;

View File

@@ -112,7 +112,7 @@ namespace BlackGui
if (this->ui->cb_withReverseLookup->isChecked()) if (this->ui->cb_withReverseLookup->isChecked())
{ {
const QString liveryString(ui->comp_LiverySelector->getRawCombinedCode()); const QString liveryString(ui->comp_LiverySelector->getRawCombinedCode());
const CAircraftModel reverseModel = CAircraftMatcher::reverseLookup(remoteAircraft.getModel(), liveryString, &msgs); const CAircraftModel reverseModel = CAircraftMatcher::reverselLookupModel(remoteAircraft.getModel(), liveryString, &msgs);
remoteAircraft.setModel(reverseModel); remoteAircraft.setModel(reverseModel);
} }
@@ -129,7 +129,7 @@ namespace BlackGui
this->m_matcher.setDefaultModel(CModelMatcherComponent::defaultModel()); this->m_matcher.setDefaultModel(CModelMatcherComponent::defaultModel());
const CSimulatedAircraft remoteAircraft(createAircraft()); const CSimulatedAircraft remoteAircraft(createAircraft());
const QString livery(ui->comp_LiverySelector->getRawCombinedCode()); const QString livery(ui->comp_LiverySelector->getRawCombinedCode());
const CAircraftModel matched = CAircraftMatcher::reverseLookup(remoteAircraft.getModel(), livery, &msgs); const CAircraftModel matched = CAircraftMatcher::reverselLookupModel(remoteAircraft.getModel(), livery, &msgs);
ui->te_Results->setText(matched.toQString(true)); ui->te_Results->setText(matched.toQString(true));
ui->tvp_ResultMessages->updateContainer(msgs); ui->tvp_ResultMessages->updateContainer(msgs);
} }

View File

@@ -52,6 +52,26 @@ namespace BlackMisc
}); });
} }
CAircraftIcaoCodeList CAircraftIcaoCodeList::findEndingWith(const QString &icaoEnding) const
{
QString ends = icaoEnding.trimmed().toUpper();
if (ends.isEmpty()) { return CAircraftIcaoCodeList(); }
CAircraftIcaoCodeList icaosDesignator;
CAircraftIcaoCodeList icaosFamily;
for (const CAircraftIcaoCode &icao : *this)
{
if (icao.getDesignator().endsWith(ends))
{
icaosDesignator.push_back(icao);
}
else if (icao.getFamily().endsWith(ends))
{
icaosFamily.push_back(icao);
}
}
return icaosDesignator.isEmpty() ? icaosFamily : icaosDesignator;
}
CAircraftIcaoCodeList CAircraftIcaoCodeList::findByIataCode(const QString &iata) const CAircraftIcaoCodeList CAircraftIcaoCodeList::findByIataCode(const QString &iata) const
{ {
if (iata.isEmpty()) { return CAircraftIcaoCodeList(); } if (iata.isEmpty()) { return CAircraftIcaoCodeList(); }
@@ -221,6 +241,10 @@ namespace BlackMisc
// we have one exact match // we have one exact match
if (codes.size() == 1) { return codes.front(); } if (codes.size() == 1) { return codes.front(); }
// now try to find as ending
codes = this->findEndingWith(d);
if (codes.size() == 1) { return codes.front(); }
// still empty, hopeless // still empty, hopeless
if (codes.isEmpty()) { return icaoPattern; } if (codes.isEmpty()) { return icaoPattern; }

View File

@@ -60,6 +60,10 @@ namespace BlackMisc
//! Find by ICAO/IATA code or family //! Find by ICAO/IATA code or family
CAircraftIcaoCodeList findByDesignatorIataOrFamily(const QString &icaoIataOrFamily) const; CAircraftIcaoCodeList findByDesignatorIataOrFamily(const QString &icaoIataOrFamily) const;
//! Find code ending with string, e.g. "738" finds "B738"
//! \remark many users use wrong ICAO designators, one typical mistake is "738" for "B737"
CAircraftIcaoCodeList findEndingWith(const QString &icaoEnding) const;
//! Find by manufacturer //! Find by manufacturer
CAircraftIcaoCodeList findByManufacturer(const QString &manufacturer) const; CAircraftIcaoCodeList findByManufacturer(const QString &manufacturer) const;

View File

@@ -119,12 +119,23 @@ namespace BlackMisc
return icaoPattern; return icaoPattern;
} }
CAirlineIcaoCode CAirlineIcaoCodeList::findBestMatchByCallsign(const CCallsign &callsign) const
{
if (this->isEmpty() || callsign.isEmpty()) { return CAirlineIcaoCode(); }
const QString airline = callsign.getAirlineSuffix().toUpper();
if (airline.isEmpty()) { return CAirlineIcaoCode(); }
const CAirlineIcaoCode airlineCode = (airline.length() == 3) ?
this->findFirstByOrDefault(&CAirlineIcaoCode::getDesignator, airline) :
this->findFirstByOrDefault(&CAirlineIcaoCode::getVDesignator, airline);
return airlineCode;
}
CAirlineIcaoCodeList CAirlineIcaoCodeList::fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete) CAirlineIcaoCodeList CAirlineIcaoCodeList::fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete)
{ {
CAirlineIcaoCodeList codes; CAirlineIcaoCodeList codes;
for (const QJsonValue &value : array) for (const QJsonValue &value : array)
{ {
CAirlineIcaoCode icao(CAirlineIcaoCode::fromDatabaseJson(value.toObject())); const CAirlineIcaoCode icao(CAirlineIcaoCode::fromDatabaseJson(value.toObject()));
if (ignoreIncomplete && !icao.hasCompleteData()) { continue; } if (ignoreIncomplete && !icao.hasCompleteData()) { continue; }
codes.push_back(icao); codes.push_back(icao);
} }

View File

@@ -13,7 +13,7 @@
#define BLACKMISC_AVIATION_AIRLINEICAOCODELIST_H #define BLACKMISC_AVIATION_AIRLINEICAOCODELIST_H
#include "airlineicaocode.h" #include "airlineicaocode.h"
#include "blackmisc/aviation/airlineicaocode.h" #include "callsign.h"
#include "blackmisc/blackmiscexport.h" #include "blackmisc/blackmiscexport.h"
#include "blackmisc/collection.h" #include "blackmisc/collection.h"
#include "blackmisc/db/datastoreobjectlist.h" #include "blackmisc/db/datastoreobjectlist.h"
@@ -72,6 +72,9 @@ namespace BlackMisc
//! Best selection by given pattern //! Best selection by given pattern
CAirlineIcaoCode smartAirlineIcaoSelector(const CAirlineIcaoCode &icaoPattern) const; CAirlineIcaoCode smartAirlineIcaoSelector(const CAirlineIcaoCode &icaoPattern) const;
//! Use callsign to conclude airline
CAirlineIcaoCode findBestMatchByCallsign(const CCallsign &callsign) const;
//! String list for completion by ICAO designator //! String list for completion by ICAO designator
QStringList toIcaoDesignatorCompleterStrings(bool combinedString = true, bool sort = true) const; QStringList toIcaoDesignatorCompleterStrings(bool combinedString = true, bool sort = true) const;

View File

@@ -61,6 +61,7 @@ namespace BlackMisc
if ("CTR" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleCenter); } if ("CTR" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleCenter); }
if ("SUP" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleSup); } if ("SUP" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleSup); }
if ("OBS" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleObs); } if ("OBS" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleObs); }
if ("INS" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleMnt); }
if ("FSS" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleFss); } if ("FSS" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleFss); }
if ("ATIS" == sfx) { return CIconList::iconByIndex(CIcons::AviationAtis); } if ("ATIS" == sfx) { return CIconList::iconByIndex(CIcons::AviationAtis); }
if ("EXAM" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleMnt); } if ("EXAM" == sfx) { return CIconList::iconByIndex(CIcons::NetworkRoleMnt); }
@@ -125,6 +126,20 @@ namespace BlackMisc
return s; return s;
} }
QString CCallsign::getAirlineSuffix() const
{
if (this->m_callsign.length() < 3) { return ""; }
if (this->isAtcCallsign()) { return ""; }
static const QRegExp regExp("^[A-Z]{3,}");
const int pos = regExp.indexIn(this->m_callsign);
if (pos < 0) { return ""; }
const QString airline = regExp.cap(0);
if (airline.length() == 3) { return airline; } // we allow 3 letters
if (airline.length() == 4 && airline.startsWith('V')) { return airline; } // we allow virtual 4 letter codes, e.g. VDLD
return ""; // invalid
}
bool CCallsign::hasSuffix() const bool CCallsign::hasSuffix() const
{ {
return this->getStringAsSet().contains('_'); return this->getStringAsSet().contains('_');
@@ -187,6 +202,8 @@ namespace BlackMisc
return this->m_callsignAsSet.compare(compareValue.m_callsignAsSet, Qt::CaseInsensitive); return this->m_callsignAsSet.compare(compareValue.m_callsignAsSet, Qt::CaseInsensitive);
case IndexTelephonyDesignator: case IndexTelephonyDesignator:
return this->m_telephonyDesignator.compare(compareValue.m_telephonyDesignator, Qt::CaseInsensitive); return this->m_telephonyDesignator.compare(compareValue.m_telephonyDesignator, Qt::CaseInsensitive);
case IndexSuffix:
return this->getSuffix().compare(compareValue.getSuffix(), Qt::CaseInsensitive);
default: default:
break; break;
} }
@@ -247,9 +264,8 @@ namespace BlackMisc
const QStringList &CCallsign::atcAlikeCallsignSuffixes() const QStringList &CCallsign::atcAlikeCallsignSuffixes()
{ {
static const QStringList a({ "ATIS", "APP", "GND", "OBS", "TWR", "DEL", "CTR", "SUP", "FSS" }); static const QStringList a({ "ATIS", "APP", "GND", "OBS", "TWR", "DEL", "CTR", "SUP", "FSS", "INS" });
return a; return a;
} }
} // namespace } // namespace
} // namespace } // namespace

View File

@@ -110,6 +110,9 @@ namespace BlackMisc
//! Get the callsign suffix ("TWR", "ATIS" ...) if any ("_" is removed) //! Get the callsign suffix ("TWR", "ATIS" ...) if any ("_" is removed)
QString getSuffix() const; QString getSuffix() const;
//! Airline suffix (e.g. DLH1234 -> DLH) if applicable
QString getAirlineSuffix() const;
//! Suffix such as "_TWR"? //! Suffix such as "_TWR"?
bool hasSuffix() const; bool hasSuffix() const;

View File

@@ -85,7 +85,7 @@ namespace BlackMisc
if (liveryPattern.hasValidDbKey()) if (liveryPattern.hasValidDbKey())
{ {
int k = liveryPattern.getDbKey(); int k = liveryPattern.getDbKey();
CLivery l(this->findByKey(k)); const CLivery l(this->findByKey(k));
if (l.hasCompleteData()) { return l; } if (l.hasCompleteData()) { return l; }
} }
@@ -93,14 +93,14 @@ namespace BlackMisc
if (liveryPattern.hasCombinedCode()) if (liveryPattern.hasCombinedCode())
{ {
QString cc(liveryPattern.getCombinedCode()); QString cc(liveryPattern.getCombinedCode());
CLivery l(this->findByCombinedCode(cc)); const CLivery l(this->findByCombinedCode(cc));
if (l.hasCompleteData()) { return l; } if (l.hasCompleteData()) { return l; }
} }
if (liveryPattern.hasValidAirlineDesignator()) if (liveryPattern.hasValidAirlineDesignator())
{ {
QString icao(liveryPattern.getAirlineIcaoCodeDesignator()); const QString icao(liveryPattern.getAirlineIcaoCodeDesignator());
CLivery l(this->findStdLiveryByAirlineIcaoDesignator(icao)); const CLivery l(this->findStdLiveryByAirlineIcaoDesignator(icao));
if (l.hasCompleteData()) { return l; } if (l.hasCompleteData()) { return l; }
} }
return CLivery(); return CLivery();

View File

@@ -334,7 +334,7 @@ namespace BlackMisc
//! From swift DB JSON //! From swift DB JSON
static CAircraftModel fromDatabaseJson(const QJsonObject &json, const QString prefix = QString("mod_")); static CAircraftModel fromDatabaseJson(const QJsonObject &json, const QString prefix = QString("mod_"));
//! Split swift network string "DLH._STD" [modelname]" //! Split swift network string "DLH._STD [modelname]"
//! \return QStringList [0] livery code , [1] model string //! \return QStringList [0] livery code , [1] model string
//! \sa getSwiftLiveryString //! \sa getSwiftLiveryString
static QStringList splitNetworkLiveryString(const QString &liveryString); static QStringList splitNetworkLiveryString(const QString &liveryString);