From 8c539fab0a5bed703f9fe503976643df0d89b624 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Tue, 2 Jan 2018 03:05:33 +0100 Subject: [PATCH] Ref T215, FP value class * split into prefix / aircraft ICAO / suffix * suffix equipment code functions * fixed FP remarks loading (JSON file) * fixed missing sending of aircraft ICAO --- samples/cliclient/client.cpp | 2 +- src/blackcore/vatsim/networkvatlib.cpp | 2 +- src/blackcore/vatsim/vatsimdatafilereader.cpp | 2 +- src/blackmisc/aviation/flightplan.cpp | 213 +++++++++++++++--- src/blackmisc/aviation/flightplan.h | 82 +++++-- 5 files changed, 258 insertions(+), 43 deletions(-) diff --git a/samples/cliclient/client.cpp b/samples/cliclient/client.cpp index 8442d83aa..5646e0408 100644 --- a/samples/cliclient/client.cpp +++ b/samples/cliclient/client.cpp @@ -511,7 +511,7 @@ namespace BlackSample { const QString rules = flightPlan.getFlightRulesAsString(); std::cout << "FLIGHTPLAN " << callsign - << flightPlan.getEquipmentIcao().toStdString() << " " << flightPlan.getOriginAirportIcao() << " " + << flightPlan.getPrefixIcaoSuffix().toStdString() << " " << flightPlan.getOriginAirportIcao() << " " << flightPlan.getDestinationAirportIcao() << " " << flightPlan.getAlternateAirportIcao() << " " << flightPlan.getTakeoffTimePlannedHourMin().toStdString() << " " << flightPlan.getTakeoffTimeActualHourMin().toStdString() << " " << flightPlan.getEnrouteTime() << " " << flightPlan.getFuelTime() << " " diff --git a/src/blackcore/vatsim/networkvatlib.cpp b/src/blackcore/vatsim/networkvatlib.cpp index 1a58e85a9..69c155e20 100644 --- a/src/blackcore/vatsim/networkvatlib.cpp +++ b/src/blackcore/vatsim/networkvatlib.cpp @@ -587,7 +587,7 @@ namespace BlackCore alt = alt.remove('.').remove(','); // remove any separators QByteArray acTypeTemp, altAptTemp, cruiseAltTemp, depAptTemp, destAptTemp, routeTemp, remarksTemp; - vatlibFP.aircraftType = acTypeTemp = toFSD(flightPlan.getEquipmentIcao()); + vatlibFP.aircraftType = acTypeTemp = toFSD(flightPlan.getPrefixIcaoSuffix()); vatlibFP.alternateAirport = altAptTemp = toFSD(flightPlan.getAlternateAirportIcao().asString()); vatlibFP.cruiseAltitude = cruiseAltTemp = toFSD(alt); vatlibFP.departAirport = depAptTemp = toFSD(flightPlan.getOriginAirportIcao().asString()); diff --git a/src/blackcore/vatsim/vatsimdatafilereader.cpp b/src/blackcore/vatsim/vatsimdatafilereader.cpp index de945d055..fbc265610 100644 --- a/src/blackcore/vatsim/vatsimdatafilereader.cpp +++ b/src/blackcore/vatsim/vatsimdatafilereader.cpp @@ -302,7 +302,7 @@ namespace BlackCore const QString equipmentCodeAndAircraft = clientPartsMap["planned_aircraft"].trimmed(); if (!equipmentCodeAndAircraft.isEmpty()) { - const QString aircraftIcaoCode = CFlightPlanRemarks::aircraftIcaoCodeFromEquipmentCode(equipmentCodeAndAircraft); + const QString aircraftIcaoCode = CFlightPlan::aircraftIcaoCodeFromEquipmentCode(equipmentCodeAndAircraft); if (CAircraftIcaoCode::isValidDesignator(aircraftIcaoCode)) { currentAircraft.setAircraftIcaoDesignator(aircraftIcaoCode); diff --git a/src/blackmisc/aviation/flightplan.cpp b/src/blackmisc/aviation/flightplan.cpp index 01fec6e9c..87b25adcc 100644 --- a/src/blackmisc/aviation/flightplan.cpp +++ b/src/blackmisc/aviation/flightplan.cpp @@ -37,13 +37,13 @@ namespace BlackMisc bool CFlightPlanRemarks::hasAnyParsedRemarks() const { - if (!this->m_isParsed) { return false; } + if (!m_isParsed) { return false; } return this->hasParsedAirlineRemarks() || m_selcalCode.isValid() || !m_voiceCapabilities.isUnknown(); } bool CFlightPlanRemarks::hasParsedAirlineRemarks() const { - if (!this->m_isParsed) { return false; } + if (!m_isParsed) { return false; } return !m_radioTelephony.isEmpty() || !m_flightOperator.isEmpty() || m_airlineIcao.hasValidDesignator(); } @@ -59,14 +59,34 @@ namespace BlackMisc return s.simplified().trimmed(); } - void CFlightPlanRemarks::parseFlightPlanRemarks() + QString CFlightPlanRemarks::textToVoiceCapabilities(const QString &text) + { + if (text.contains("TEXT", Qt::CaseInsensitive)) { return QStringLiteral("/T/"); } + if (text.contains("RECEIVE", Qt::CaseInsensitive)) { return QStringLiteral("/R/"); } + if (text.contains("VOICE", Qt::CaseInsensitive)) { return QStringLiteral("/V/"); } + return QStringLiteral(""); + } + + QString CFlightPlanRemarks::replaceVoiceCapabilities(const QString &newCaps, const QString &oldRemarks) + { + if (newCaps.isEmpty()) { return oldRemarks; } + if (oldRemarks.isEmpty()) { return newCaps; } + + QString r(oldRemarks); + if (r.contains("/V/", Qt::CaseInsensitive)) { r.replace("/V/", newCaps, Qt::CaseInsensitive); return r; } + if (r.contains("/R/", Qt::CaseInsensitive)) { r.replace("/R/", newCaps, Qt::CaseInsensitive); return r; } + if (r.contains("/T/", Qt::CaseInsensitive)) { r.replace("/T/", newCaps, Qt::CaseInsensitive); return r; } + return newCaps + " " + r; + } + + void CFlightPlanRemarks::parseFlightPlanRemarks(bool force) { // examples: VFPS = VATSIM Flightplan Prefile System // 1) RT/KESTREL OPR/MYTRAVEL REG/G-DAJC SEL/FP-ES PER/C NAV/RNP10 // 2) OPR/UAL CALLSIGN/UNITED // 3) /v/FPL-VIR9-IS-A346/DEP/S-EGLL/ARR/KJFK/REG/G-VGAS/TCAS RVR/200 OPR/VIRGIN AIRLINES - if (m_isParsed) { return; } + if (!force && m_isParsed) { return; } m_isParsed = true; if (m_remarks.isEmpty()) { return; } const QString remarks = m_remarks.toUpper(); @@ -85,15 +105,6 @@ namespace BlackMisc } } - QString CFlightPlanRemarks::aircraftIcaoCodeFromEquipmentCode(const QString &equipmentCodeAndAircraft) - { - // http://uk.flightaware.com/about/faq_aircraft_flight_plan_suffix.rvt - // we expect something like H/B772/F B773 B773/F - thread_local const QRegularExpression reg("/."); - QString aircraftIcaoCode(equipmentCodeAndAircraft); - aircraftIcaoCode = aircraftIcaoCode.replace(reg, "").trimmed().toUpper(); - return aircraftIcaoCode; - } QString CFlightPlanRemarks::cut(const QString &remarks, const QString &marker) { @@ -125,7 +136,7 @@ namespace BlackMisc const PhysicalQuantities::CTime &fuelTime, const CAltitude &cruiseAltitude, const PhysicalQuantities::CSpeed &cruiseTrueAirspeed, CFlightPlan::FlightRules flightRules, const QString &route, const QString &remarks) : m_callsign(callsign), - m_equipmentIcao(equipmentIcao), m_originAirportIcao(originAirportIcao), m_destinationAirportIcao(destinationAirportIcao), m_alternateAirportIcao(alternateAirportIcao), + m_equipmentSuffix(equipmentIcao), m_originAirportIcao(originAirportIcao), m_destinationAirportIcao(destinationAirportIcao), m_alternateAirportIcao(alternateAirportIcao), m_takeoffTimePlanned(takeoffTimePlanned), m_takeoffTimeActual(takeoffTimeActual), m_enrouteTime(enrouteTime), m_fuelTime(fuelTime), m_cruiseAltitude(cruiseAltitude), m_cruiseTrueAirspeed(cruiseTrueAirspeed), m_flightRules(flightRules), m_route(route.trimmed().left(MaxRouteLength).toUpper()), @@ -144,9 +155,10 @@ namespace BlackMisc void CFlightPlan::setEquipmentIcao(const QString &equipmentIcao) { - m_equipmentIcao = equipmentIcao; - const QString aircraftIcao = CFlightPlanRemarks::aircraftIcaoCodeFromEquipmentCode(equipmentIcao); - m_aircraftIcao = CAircraftIcaoCode::isValidDesignator(aircraftIcao) ? aircraftIcao : ""; + const QStringList parts = CFlightPlan::splitEquipmentCode(equipmentIcao); + m_aircraftIcao = CAircraftIcaoCode::isValidDesignator(parts[1]) ? parts[1] : ""; + m_equipmentPrefix = parts[0]; + m_equipmentSuffix = parts[2]; } void CFlightPlan::setRemarks(const QString &remarks) @@ -156,20 +168,21 @@ namespace BlackMisc CFlightPlan::FlightRules CFlightPlan::getFlightRulesAsVFRorIFR() const { - switch (getFlightRules()) + switch (this->getFlightRules()) { - case IFR: - return IFR; + case IFR: return IFR; case VFR: case SVFR: - case DVFR: - return VFR; + case DVFR: return VFR; case UNKNOWN: - default: - break; + default: break; } return UNKNOWN; + } + QString CFlightPlan::getPrefixIcaoSuffix() const + { + return CFlightPlan::concatPrefixIcaoSuffix(m_equipmentPrefix, m_aircraftIcao.getDesignator(), m_equipmentSuffix); } CVariant CFlightPlan::propertyByIndex(const CPropertyIndex &index) const @@ -183,7 +196,7 @@ namespace BlackMisc case IndexAlternateAirportIcao: return m_alternateAirportIcao.propertyByIndex(index.copyFrontRemoved()); case IndexDestinationAirportIcao: return m_destinationAirportIcao.propertyByIndex(index.copyFrontRemoved()); case IndexOriginAirportIcao: return m_originAirportIcao.propertyByIndex(index.copyFrontRemoved()); - case IndexCallsign: return this->m_callsign.propertyByIndex(index.copyFrontRemoved()); + case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved()); case IndexRemarks: return CVariant::from(m_remarks); default: return CValueObject::propertyByIndex(index); } @@ -213,7 +226,7 @@ namespace BlackMisc QString CFlightPlan::convertToQString(bool i18n) const { const QString s = m_callsign.toQString(i18n) - % QLatin1Char(' ') % m_equipmentIcao + % QLatin1Char(' ') % m_equipmentSuffix % QLatin1Char(' ') % m_originAirportIcao.toQString(i18n) % QLatin1Char(' ') % m_destinationAirportIcao.toQString(i18n) % QLatin1Char(' ') % m_alternateAirportIcao.toQString(i18n) @@ -241,6 +254,71 @@ namespace BlackMisc } } + QString CFlightPlan::aircraftIcaoCodeFromEquipmentCode(const QString &equipmentCodeAndAircraft) + { + // http://uk.flightaware.com/about/faq_aircraft_flight_plan_suffix.rvt + // we expect something like H/B772/F B773 B773/F + thread_local const QRegularExpression reg("/."); + QString aircraftIcaoCode(equipmentCodeAndAircraft); + aircraftIcaoCode = aircraftIcaoCode.replace(reg, "").trimmed().toUpper(); + return aircraftIcaoCode; + } + + QStringList CFlightPlan::splitEquipmentCode(const QString &equipmentCodeAndAircraft) + { + static const QStringList empty({"", "", ""}); + if (empty.isEmpty()) { return empty; } + QStringList split = equipmentCodeAndAircraft.split('/'); + if (split.length() == 3) { return split; } // "H/B738/F" + if (split.length() == 2) + { + if (split[0].length() == 1) + { + // we assume prefix + ICAO + // e.g. "H/B748" + split.push_back(""); + } + else + { + // we assume ICAO + suffix + // e.g. "B748/F" + split.push_front(""); + return split; + } + } + + // one part only + if (split[0].length() > 1 && CAircraftIcaoCode::isValidDesignator(split[0])) + { + QStringList sl(empty); + sl[1] = split[0]; // only ICAO + return sl; + } + if (split[0].length() != 1) { return empty; } // something invalid + + // one part, one char only. hard to tell + QStringList sl(empty); + if (faaEquipmentCodes().contains(split[0])) + { + sl[2] = split[0]; // return as equipment code + return sl; + } + sl[0] = split[0]; + return sl; + } + + QString CFlightPlan::concatPrefixIcaoSuffix(const QString &prefix, const QString &icao, const QString &suffix) + { + QString s = prefix; + if (!icao.isEmpty()) + { + s += (s.isEmpty() ? QStringLiteral("") : QStringLiteral("/")) % icao; + } + if (suffix.isEmpty()) { return s; } + if (s.isEmpty()) { return suffix; } + return s % QStringLiteral("/") % suffix; + } + CFlightPlan::FlightRules CFlightPlan::stringToFlightRules(const QString &flightRules) { if (flightRules.length() < 3) { return UNKNOWN; } @@ -252,6 +330,89 @@ namespace BlackMisc return UNKNOWN; } + const QStringList &CFlightPlan::faaEquipmentCodes() + { + // List of FAA Aircraft Equipment Codes For US Domestic Flights + static const QStringList e({"X", "T", "U", "D", "B", "A", "M", "N", "P", "Y", "C", "I", "L", "G", "Z", "I", "W", "L"}); + return e; + } + + const QStringList &CFlightPlan::faaEquipmentCodesInfo() + { + static const QStringList e( + { + "X No transponder", + "T Transponder with no Mode C", + "U Transponder with Mode C", + "D DME: No transponder", + "B DME: Transponder with no Mode C", + "A DME: Transponder with Mode C", + "M TACAN only: No transponder", + "N TACAN only: Transponder with no Mode C", + "P TACAN only: Transponder with Mode C", + "Y Basic RNAV: LORAN, VOR/DME, or INS with no transponder", + "C Basic RNAV: LORAN, VOR/DME, or INS, transponder with no Mode C", + "I Basic RNAV: LORAN, VOR/DME, or INS, transponder with Mode C", + "L Advanced RNAV: RNAV capability with Global Navigation Satellite System (GNSS)", + "G Advanced RNAV: RNAV capability with GNSS and without RVSM", + "Z Advanced RNAV: RNAV capability without GNSS and with RVSM", + "I Advanced RNAV: RNAV capability without GNSS and without RVSM", + "W RVSM", + "L RVSM and /G" + }); + return e; + } + + const QStringList &CFlightPlan::squawkBoxEquipmentCodes() + { + static const QStringList e({"E", "F", "G", "R", "J", "K", "L", "Q"}); + return e; + } + + const QStringList &CFlightPlan::squawkBoxEquipmentCodesInfo() + { + static const QStringList e( + { + "E Flight Management System (FMS) with DME/DME and IRU positioning updating", + "F Flight Management System (FMS) with DME/DME positioning updating", + "G Global Navigation Satellite System (GNSS), including GPS or Wide Area Augmentation System", + "R Required navigation performance, the aircraft meets the RNP type prescribed for the route segment(s), route(s) and or area concerned", + "J RVSM + E", + "L RVSM + F", + "L RVSM + G", + "Q RVSM + E" + }); + return e; + } + + const QStringList &CFlightPlan::equipmentCodes() + { + static const QStringList e = [] + { + QSet el(CFlightPlan::faaEquipmentCodes().toSet()); + el.unite(CFlightPlan::squawkBoxEquipmentCodes().toSet()); + return el.toList(); + }(); + return e; + } + + const QStringList &CFlightPlan::equipmentCodesInfo() + { + static const QStringList e = [] + { + QStringList info(CFlightPlan::faaEquipmentCodesInfo()); + info.append(CFlightPlan::squawkBoxEquipmentCodesInfo()); + return info; + }(); + return e; + } + + const QStringList &CFlightPlan::prefixCodes() + { + static const QStringList p({"T", "H"}); + return p; + } + CIcon CFlightPlan::toIcon() const { return CIcon::iconByIndex(CIcons::StandardIconAppFlightPlan16); diff --git a/src/blackmisc/aviation/flightplan.h b/src/blackmisc/aviation/flightplan.h index 6a9b101c2..e7fea6928 100644 --- a/src/blackmisc/aviation/flightplan.h +++ b/src/blackmisc/aviation/flightplan.h @@ -83,7 +83,7 @@ namespace BlackMisc bool hasParsedAirlineRemarks() const; //! Parse remarks from a flight plan - void parseFlightPlanRemarks(); + void parseFlightPlanRemarks(bool force = false); //! Valid airline ICAO? //! \remark valid here means valid syntax, no guarantee it really exists @@ -98,15 +98,17 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::String::toQString() QString convertToQString(bool i18n = false) const; - //! Get aircraft ICAO code from equipment code like - //! \remark we expect something like H/B772/F B773 B773/F - static QString aircraftIcaoCodeFromEquipmentCode(const QString &equipmentCodeAndAircraft); + //! Turn text into voice capabilities for remarks + static QString textToVoiceCapabilities(const QString &text); + + //! Replace the voice capabilities remarks part + static QString replaceVoiceCapabilities(const QString &newCaps, const QString &oldRemarks); private: QString m_remarks; //!< the unparsed string QString m_radioTelephony; //!< radio telephony designator QString m_flightOperator; //!< operator, i.e. normally the airline name - CCallsign m_registration; //!< callsign of other pilot + CCallsign m_registration; //!< callsign of pilot CSelcal m_selcalCode; //!< SELCAL code CAirlineIcaoCode m_airlineIcao; //!< airline ICAO if provided in flight plan Network::CVoiceCapabilities m_voiceCapabilities; //!< voice capabilities @@ -114,6 +116,7 @@ namespace BlackMisc BLACK_METACLASS( CFlightPlanRemarks, + BLACK_METAMEMBER(remarks, 0, DisabledForComparison), BLACK_METAMEMBER(radioTelephony), BLACK_METAMEMBER(flightOperator), BLACK_METAMEMBER(airlineIcao), @@ -170,7 +173,8 @@ namespace BlackMisc //! Callsign (of aircraft) void setCallsign(const CCallsign &callsign); - //! Set ICAO aircraft equipment code string (e.g. "T/A320/F") + //! Set single char ICAO aircraft equipment code like used in "T/A320/F" (here "F") + //! \remark function can handle full codes like "T/A320/F" of just the "F" void setEquipmentIcao(const QString &equipmentIcao); //! Set origin airport ICAO code @@ -236,9 +240,6 @@ namespace BlackMisc //! Has callsign? bool hasCallsign() const { return !m_callsign.isEmpty(); } - //! Get ICAO aircraft equipment code string - const QString &getEquipmentIcao() const { return m_equipmentIcao; } - //! Get origin airport ICAO code const CAirportIcaoCode &getOriginAirportIcao() const { return m_originAirportIcao; } @@ -273,7 +274,7 @@ namespace BlackMisc QString getFuelTimeHourMin() const { return m_fuelTime.valueRoundedWithUnit(BlackMisc::PhysicalQuantities::CTimeUnit::hrmin()); } //! Cruising altitudes - const BlackMisc::Aviation::CAltitude &getCruiseAltitude() const { return m_cruiseAltitude; } + const CAltitude &getCruiseAltitude() const { return m_cruiseAltitude; } //! Get planned cruise TAS const PhysicalQuantities::CSpeed &getCruiseTrueAirspeed() const { return m_cruiseTrueAirspeed; } @@ -305,12 +306,30 @@ namespace BlackMisc //! Get the parsable remarks const CFlightPlanRemarks &getFlightPlanRemarks() const { return m_remarks; } - //! Get aircraft ICAO, derived from equipment ICAO as in getEquipmentIcao() + //! Get ICAO aircraft equipment prefix H/B737/F "H" + const QString &getEquipmentPrefix() const { return m_equipmentPrefix; } + + //! Set ICAO aircraft equipment prefix H/B737/F "H" + void setEquipmentPrefix(const QString &prefix) { m_equipmentPrefix = prefix; } + + //! Get ICAO aircraft equipment suffix H/B737/F "F" + const QString &getEquipmentSuffix() const { return m_equipmentSuffix; } + + //! Set ICAO aircraft equipment suffix H/B737/F "F" + void setEquipmentSuffix(const QString &suffix) { m_equipmentSuffix = suffix; } + + //! Get aircraft ICAO H/B737/F "B737" const CAircraftIcaoCode &getAircraftIcao() const { return m_aircraftIcao; } + //! Set aircraft ICAO code H/B737/F "B737" + void setAircraftIcao(const CAircraftIcaoCode &icao) { m_aircraftIcao = icao; } + //! Has aircraft ICAO? bool hasAircraftIcao() const { return m_aircraftIcao.hasDesignator(); } + //! Full string like "H/B737/F" + QString getPrefixIcaoSuffix() const; + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex CVariant propertyByIndex(const CPropertyIndex &index) const; @@ -329,10 +348,42 @@ namespace BlackMisc //! String to flight rules static FlightRules stringToFlightRules(const QString &flightRules); + //! Get aircraft ICAO code from equipment code like + //! \remark we expect something like "H/B772/F" "B773" "B773/F" + static QString aircraftIcaoCodeFromEquipmentCode(const QString &equipmentCodeAndAircraft); + + //! Get the 3 parts of "H/B772/F", returned as prefix, ICAO, suffix + static QStringList splitEquipmentCode(const QString &equipmentCodeAndAircraft); + + //! Concat the 3 parts to "H/B772/F" + static QString concatPrefixIcaoSuffix(const QString &prefix, const QString &icao, const QString &suffix); + + //! Equipment codes 1 character + static const QStringList &faaEquipmentCodes(); + + //! Codes plus info + static const QStringList &faaEquipmentCodesInfo(); + + //! SquawkBox equipment codes + static const QStringList &squawkBoxEquipmentCodes(); + + //! Codes plus info + static const QStringList &squawkBoxEquipmentCodesInfo(); + + //! All equipment codes + static const QStringList &equipmentCodes(); + + //! Equipment codes info + static const QStringList &equipmentCodesInfo(); + + //! Prefix codes "H" .. Heavy, "T" .. TCAS + static const QStringList &prefixCodes(); + private: CCallsign m_callsign; //!< aircraft callsign - QString m_equipmentIcao; //!< e.g. "T/A320/F" - CAircraftIcaoCode m_aircraftIcao; //!< Aircraft ICAO code derived from equipment ICAO + CAircraftIcaoCode m_aircraftIcao; //!< Aircraft ICAO code + QString m_equipmentPrefix; //!< e.g. "T/A320/F" -> the "T" + QString m_equipmentSuffix; //!< e.g. "T/A320/F" -> the "F" CAirportIcaoCode m_originAirportIcao; CAirportIcaoCode m_destinationAirportIcao; CAirportIcaoCode m_alternateAirportIcao; @@ -348,7 +399,10 @@ namespace BlackMisc BLACK_METACLASS( CFlightPlan, - BLACK_METAMEMBER(equipmentIcao), + // callsign will be current flight + BLACK_METAMEMBER(aircraftIcao), + BLACK_METAMEMBER(equipmentPrefix), + BLACK_METAMEMBER(equipmentSuffix), BLACK_METAMEMBER(originAirportIcao), BLACK_METAMEMBER(destinationAirportIcao), BLACK_METAMEMBER(alternateAirportIcao),