7 #include <QDomDocument>
9 #include <QRegularExpression>
10 #include <QStringBuilder>
23 using namespace swift::misc::network;
24 using namespace swift::misc::physical_quantities;
29 namespace swift::misc::aviation
31 CFlightPlanRemarks::CFlightPlanRemarks() {}
33 CFlightPlanRemarks::CFlightPlanRemarks(
const QString &remarks,
bool parse) : m_remarks(cleanRemarks(remarks))
39 : m_remarks(cleanRemarks(remarks)), m_voiceCapabilities(voiceCapabilities)
46 if (m_selcalCode == selcal || selcal.length() != 4) {
return false; }
48 CFlightPlanRemarks::replaceRemark(m_remarks, QStringLiteral(
"SEL/"), QStringLiteral(
"SEL/%1").arg(selcal));
55 m_voiceCapabilities = capabilities;
63 if (!m_isParsed) {
return false; }
69 if (!m_isParsed) {
return false; }
70 return !m_radioTelephony.isEmpty() || !m_flightOperator.isEmpty() || m_airlineIcao.
hasValidDesignator();
75 const QString s = (m_registration.
isEmpty() ? QString() : u
"reg.: " % m_registration.
toQString(i18n)) %
77 (m_radioTelephony.isEmpty() ? QString() : u
" radio tel.:" % m_radioTelephony) %
78 (m_flightOperator.isEmpty() ? QString() : u
" operator: " % m_flightOperator) %
79 (!m_selcalCode.
isValid() ? QString() : u
" SELCAL: " % m_selcalCode.
getCode()) % u
" voice: " %
81 return s.simplified().trimmed();
92 if (newCapRemarks.isEmpty()) {
return oldRemarks; }
93 if (oldRemarks.isEmpty()) {
return newCapRemarks; }
95 QString r(oldRemarks);
96 if (r.contains(
"/V/", Qt::CaseInsensitive))
98 r.replace(
"/V/", newCapRemarks, Qt::CaseInsensitive);
101 if (r.contains(
"/R/", Qt::CaseInsensitive))
103 r.replace(
"/R/", newCapRemarks, Qt::CaseInsensitive);
106 if (r.contains(
"/T/", Qt::CaseInsensitive))
108 r.replace(
"/T/", newCapRemarks, Qt::CaseInsensitive);
111 return newCapRemarks % u
' ' % r;
116 QString r = remarksIn;
129 if (!force && m_isParsed) {
return; }
131 if (m_remarks.isEmpty()) {
return; }
132 const QString remarks = m_remarks.toUpper();
133 const QString callsign =
138 this->getRemark(remarks,
"OPR/");
139 m_selcalCode =
CSelcal(this->getRemark(remarks,
"SEL/"));
140 m_radioTelephony = getRemark(remarks,
"CALLSIGN/");
141 if (m_radioTelephony.isEmpty()) { m_radioTelephony = getRemark(remarks,
"RT/"); }
146 m_flightOperator.clear();
150 QString CFlightPlanRemarks::getRemark(
const QString &remarks,
const QString &marker)
152 const int maxIndex = remarks.size() - 1;
153 int f = remarks.indexOf(marker);
154 if (f < 0) {
return {}; }
155 f += marker.length();
156 if (maxIndex <= f) {
return {}; }
163 thread_local
const QRegularExpression nextMarker(
"\\s+\\w*/|$");
164 int to1 = remarks.indexOf(nextMarker, f + 1);
165 if (to1 < 0) { to1 = maxIndex + 1; }
166 int to2 = remarks.indexOf(
'/', f + 1);
167 if (to2 < 0) { to2 = maxIndex + 1; }
168 const int to = qMin(to1, to2);
169 const QString cut = remarks.mid(f, to - f).simplified();
173 QString CFlightPlanRemarks::replaceRemark(
const QString &remarks,
const QString &marker,
const QString &newRemark)
176 const int maxIndex = remarks.size() - 1;
177 int f = remarks.indexOf(marker);
180 f += marker.length();
181 if (maxIndex <= f) {
return remarks; }
182 thread_local
const QRegularExpression nextMarker(
"\\s+\\w*/|$");
183 int to1 = remarks.indexOf(nextMarker, f + 1);
184 if (to1 < 0) { to1 = maxIndex + 1; }
185 int to2 = remarks.indexOf(
'/', f + 1);
186 if (to2 < 0) { to2 = maxIndex + 1; }
187 const int to = qMin(to1, to2);
190 return r.isEmpty() ? newRemark : r % u
" " % newRemark;
201 const CAirportIcaoCode &alternateAirportIcao,
const QDateTime &takeoffTimePlanned,
206 : m_callsign(callsign), m_aircraftInfo(aircraftInfo), m_originAirportIcao(originAirportIcao),
207 m_destinationAirportIcao(destinationAirportIcao), m_alternateAirportIcao(alternateAirportIcao),
208 m_takeoffTimePlanned(takeoffTimePlanned), m_takeoffTimeActual(takeoffTimeActual), m_enrouteTime(enrouteTime),
209 m_fuelTime(fuelTime), m_cruiseAltitude(cruiseAltitude), m_cruiseTrueAirspeed(cruiseTrueAirspeed),
210 m_flightRules(flightRules), m_route(route.trimmed().left(MaxRouteLength).toUpper()),
211 m_remarks(remarks.trimmed().left(MaxRemarksLength).toUpper())
220 m_callsign = callsign;
228 m_takeoffTimePlanned = takeoffTimePlanned.toUTC();
233 m_takeoffTimeActual = takeoffTimeActual.toUTC();
263 if (index.
isMyself()) {
return QVariant::fromValue(*
this); }
273 case IndexRemarks:
return QVariant::fromValue(m_remarks);
294 case IndexAlternateAirportIcao:
297 case IndexDestinationAirportIcao:
302 case IndexRemarks: this->
setRemarks(variant.toString());
break;
311 QString CFlightPlan::buildString(
bool i18n,
const QString &separator)
const
314 m_callsign.
toQString(i18n) % u
" aircraft: " % m_aircraftInfo.
asIcaoString() % separator % u
"origin: " %
315 m_originAirportIcao.
toQString(i18n) % u
" destination: " % m_destinationAirportIcao.
toQString(i18n) %
316 u
" alternate: " % m_alternateAirportIcao.
toQString(i18n) % separator % u
"takeoff planed: " %
317 m_takeoffTimePlanned.toString(
"ddhhmm") % u
" actual: " % m_takeoffTimeActual.toString(
"ddhhmm") %
318 separator % u
"enroute time: " % m_enrouteTime.
toQString(i18n) % u
" fuel time:" %
319 m_fuelTime.
toQString(i18n) % separator % u
"altitude: " % m_cruiseAltitude.
toQString(i18n) % u
" speed: " %
320 m_cruiseTrueAirspeed.
toQString(i18n) % separator % u
"route: " % m_route % separator % u
"remarks: " %
330 const QString altStr = values.value(
"Altitude");
331 const CAltitude alt(altStr.length() < 4 ?
"FL" + altStr : altStr +
"ft");
333 const QString type = values.value(
"Type");
336 const int fuelMins = values.value(
"FuelMinutes").toInt() + 60 * values.value(
"FuelHours").toInt();
337 const CTime fuelTime(fuelMins, CTimeUnit::min());
339 const int enrouteMins = values.value(
"FlightMinutes").toInt() + 60 * values.value(
"FlightHours").toInt();
340 const CTime enrouteTime(enrouteMins, CTimeUnit::min());
354 int airspeedKts = values.value(
"Airspeed").toInt();
355 const CSpeed airspeed(airspeedKts, CSpeedUnit::kts());
368 doc.setContent(simBrief);
369 const QDomNodeList originList = doc.elementsByTagName(
"origin");
370 if (!originList.isEmpty())
372 const QDomNode origin = originList.at(0);
373 const QString icao = origin.firstChildElement(
"icao_code").text();
376 const QDomNodeList destList = doc.elementsByTagName(
"destination");
377 if (!destList.isEmpty())
379 const QDomNode dest = destList.at(0);
380 const QString icao = dest.firstChildElement(
"icao_code").text();
383 const QDomNodeList altList = doc.elementsByTagName(
"alternate");
384 if (!altList.isEmpty())
386 const QDomNode alternate = altList.at(0);
387 const QString icao = alternate.firstChildElement(
"icao_code").text();
390 const QDomNodeList generalList = doc.elementsByTagName(
"general");
391 if (!generalList.isEmpty())
394 const QDomNode general = generalList.at(0);
395 QString route = general.firstChildElement(
"route").text();
396 fp.
setRoute(route.remove(
"DCT").simplified().trimmed());
397 const QString airline = general.firstChildElement(
"icao_airline").text();
398 const QString flightNumber = general.firstChildElement(
"flight_number").text();
400 const QString cruiseAlt = general.firstChildElement(
"initial_altitude").text();
401 const int cruiseAltFt = cruiseAlt.toInt(&ok);
405 if (cruiseAlt.endsWith(
"00") && cruiseAltFt > 5000) { ca.
toFlightLevel(); }
411 const QString tas = general.firstChildElement(
"cruise_tas").text();
412 const int tasKts = tas.toInt(&ok);
416 const QDomNodeList timeList = doc.elementsByTagName(
"times");
417 if (!timeList.isEmpty())
420 const QDomNode times = timeList.at(0);
421 const QString enroute = times.firstChildElement(
"est_time_enroute").text();
422 const int enrouteSecs = enroute.toInt(&ok);
424 const QString endurance = times.firstChildElement(
"endurance").text();
425 const int enduranceSecs = endurance.toInt(&ok);
427 const QString depTime = times.firstChildElement(
"sched_out").text();
428 const int depTimeUnixTs = depTime.toInt(&ok);
431 QDateTime depTs = QDateTime::fromSecsSinceEpoch(depTimeUnixTs, QTimeZone::utc());
432 depTs.setTimeZone(QTimeZone::utc());
437 const QDomNodeList aircraftList = doc.elementsByTagName(
"aircraft");
438 if (!aircraftList.isEmpty())
440 const QDomNode aircraft = aircraftList.at(0);
441 const QString equipment = aircraft.firstChildElement(
"equip").text();
443 const int b = equipment.indexOf(
'-');
444 const int e = equipment.indexOf(
'/');
446 if (e > b && e >= 0 && b >= 0 && equipment.size() > e)
450 const QChar wtcChar = equipment.mid(0, 1).at(0);
453 const QString comNavEquipmentString = equipment.mid(b + 1, e - b - 1);
456 const QString ssrEquipmentString = equipment.mid(e + 1);
465 bool remarksChanged =
false;
466 const QString selcal = aircraft.firstChildElement(
"selcal").text();
467 if (selcal.length() == 4)
470 remarksChanged = c || remarksChanged;
482 if (fileSuffix.contains(
"xml", Qt::CaseInsensitive))
484 if (data.contains(
"<OFP>", Qt::CaseInsensitive) && data.contains(
"<general>", Qt::CaseInsensitive))
498 QFileInfo fi(fileName);
499 if (fileName.isEmpty())
515 .validationError(u
"File '%1' does not exist")
528 .validationError(u
"File '%1' does not contain data")
535 if (fileName.endsWith(
".json", Qt::CaseInsensitive))
539 if (!json::looksLikeSwiftJson(data))
542 u
"Reading '%1' yields no data",
true)
551 if (json::looksLikeSwiftTypeValuePairJson(jsonObject))
564 u
"Wrong format for flight plan in '%1'")
578 "Parse error in " + fileName);
594 QStringLiteral(
"Parsing flight plan from '%1' failed.").arg(fileName)));
602 static const QString v(
"VFR");
603 static const QString i(
"IFR");
604 static const QString s(
"SVFR");
605 static const QString d(
"DVFR");
606 static const QString unknown(
"???");
624 if (fr.startsWith(
"DVFR")) {
return DVFR; }
625 if (fr.startsWith(
"SVFR")) {
return SVFR; }
626 if (fr.startsWith(
"VFR")) {
return VFR; }
627 if (fr.startsWith(
"IFR")) {
return IFR; }
633 static const QStringList r({
"IFR",
"VFR",
"SVFR",
"DVFR" });
static QString readFileToString(const QString &fileNameAndPath)
Read file into string.
IconIndex
Index for each icon, allows to send them via DBus, efficiently store them, etc.
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
static const QString & flightPlan()
Flight plan.
Non-owning reference to a CPropertyIndex with a subset of its features.
Q_REQUIRED_RESULT CPropertyIndexRef copyFrontRemoved() const
Copy with first element removed.
CastType frontCasted() const
First element casted to given type, usually the PropertIndex enum.
bool isMyself() const
Myself index, used with nesting.
void push_back(const T &value)
Appends an element at the end of the sequence.
Streamable status message, e.g.
static CStatusMessage fromJsonException(const CJsonException &ex, const CLogCategoryList &categories, const QString &prefix)
Object from JSON exception message.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
Wrapper around QVariant which provides transparent access to CValueObject methods of the contained ob...
T value() const
Return the value converted to the type T.
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
bool canConvert(int typeId) const
True if this variant can be converted to the type with the given metatype ID.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
static bool canHandleIndex(CPropertyIndexRef index)
Can given index be handled.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Value object for ICAO classification.
static bool isValidAirlineDesignator(const QString &airline)
Valid designator?
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasValidDesignator() const
Airline designator available?
Value object encapsulating information of airport ICAO data.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Altitude as used in aviation, can be AGL or MSL altitude.
bool toFlightLevel()
MSL to flightlevel.
Value object encapsulating information of a callsign.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
static QString unifyCallsign(const QString &callsign, TypeHint hint=NoHint)
Unify the callsign by removing illegal characters.
bool isEmpty() const
Is empty?
static bool isValidAircraftCallsign(const QString &callsign)
Valid callsign?
void setTypeHint(TypeHint hint)
Type hint.
ICAO flightplan field 10a.
Flightplan-related information about an aircraft (aircraft ICAO, equipment and WTC)
QString asIcaoString() const
Full string in ICAO format: "AIRCRAFT_ICAO/WTC-EQUIPMENT/SSR".
CAircraftIcaoCode getAircraftIcao() const
Get Aircraft ICAO.
Value object for a flight plan.
QString asHTML(bool i18n=false) const
As HTML.
static bool isVFRRules(FlightRules rule)
Is rule a VFR rule?
void setAircraftInfo(const CFlightPlanAircraftInfo &aircraftInfo)
Set information about the aircraft used in this flightplan.
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
void setFlightPlanRemarks(const CFlightPlanRemarks &remarks)
Set FP remarks.
CFlightPlan()=default
Default constructor.
void setFlightRule(FlightRules flightRule)
Set flight rules (VFR or IFR)
static FlightRules stringToFlightRules(const QString &flightRules)
String to flight rules.
QString getTakeoffTimePlannedHourMin() const
Get planned takeoff time (planned)
const CFlightPlanRemarks & getFlightPlanRemarks() const
Get the parsable remarks.
ColumnIndex
Properties by index.
void setVoiceCapabilities(const QString &capabilities)
Set voice capabilities.
void setOriginAirportIcao(const QString &originAirportIcao)
Set origin airport ICAO code.
void setTakeoffTimePlanned(const QDateTime &takeoffTimePlanned)
Set planned takeoff time.
void setCruiseTrueAirspeed(const physical_quantities::CSpeed &cruiseTrueAirspeed)
Set planned cruise TAS.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
void setAlternateAirportIcao(const QString &alternateAirportIcao)
Set alternate destination airport ICAO code.
void setFuelTime(const physical_quantities::CTime &fuelTime)
Set amount of fuel load in time.
static const QStringList & flightRules()
All rules as string.
static CFlightPlan fromSimBriefFormat(const QString &simBrief)
From SimBrief format (XML)
FlightRules
Flight rules (VFR or IFR)
@ IFR
Instrument flight rules.
@ VFR
Visual flight rules.
@ SVFR
Special VFR (reserved for ATC use),.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
static CFlightPlan fromMultipleFormats(const QString &data, const QString &fileSuffix)
From multiple formats.
static const QString & flightRulesToString(FlightRules rules)
Rules to string.
void setTakeoffTimeActual(const QDateTime &takeoffTimeActual)
Set actual takeoff time (reserved for ATC use)
const QString & getRemarks() const
Get remarks string.
void setCruiseAltitudeString(const QString &altitudeString)
Cruising altitude already as string.
QString getTakeoffTimeActualHourMin() const
Get actual takeoff time (actual)
void setRemarks(const QString &remarks)
Set remarks string (max 100 characters)
static const QStringList & getLogCategories()
The log. catgeories.
QString convertToQString(bool i18n=false) const
Cast as QString.
void setEnrouteTime(const physical_quantities::CTime &enrouteTime)
Set planned enroute flight time.
static CFlightPlan fromSB4Format(const QString &sbData)
From SB4 data.
void setRoute(const QString &route)
Set route string.
static bool isIFRRules(FlightRules rule)
Is rule a IFR rule?
static constexpr int MaxRouteLength
Max.route length.
void setDestinationAirportIcao(const QString &destinationAirportIcao)
Set destination airport ICAO code.
void setCruiseAltitude(const CAltitude &cruiseAltitude)
Set planned cruise altitude.
CFlightPlanAircraftInfo getAircraftInfo() const
Get ICAO aircraft NAV/COM equipment.
static CFlightPlan loadFromMultipleFormats(const QString &fileName, CStatusMessageList *msgs=nullptr)
Load from multiple formats.
void setCallsign(const CCallsign &callsign)
Callsign (of aircraft)
bool isValid() const
Is valid?
const QString & getCode() const
Get SELCAL code.
ICAO flightplan field 10b.
ICAO wake turbulence category.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
QString toQString(bool i18n=false) const
Cast as QString.
Value object encapsulating information for voice capabilities.
bool isUnknown() const
Is capability known.
const QString & toFlightPlanRemarks() const
To flight plan remarks.
PQ & switchUnit(const MU &newUnit)
Change unit, and convert value to maintain the same quantity.
static CTimeUnit hrmin()
Hours, minutes.
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
SWIFT_MISC_EXPORT QMap< QString, QString > parseIniValues(const QString &data)
Obtain ini file like values, e.g. foo=bar.
SWIFT_MISC_EXPORT QString simplifyAccents(const QString &candidate)
Remove accents / diacritic marks from a string.
QString asciiOnlyString(const QString &string)
String only with ASCII values.
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.