swift
flightplan.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDateTime>
7 #include <QDomDocument>
8 #include <QFile>
9 #include <QRegularExpression>
10 #include <QStringBuilder>
11 #include <QTimeZone>
12 
14 #include "misc/aviation/altitude.h"
15 #include "misc/fileutils.h"
16 #include "misc/iconlist.h"
17 #include "misc/json.h"
18 #include "misc/pq/speed.h"
19 #include "misc/pq/time.h"
20 #include "misc/propertyindexvariantmap.h" // needed for mixin::Index::apply
21 #include "misc/stringutils.h"
22 
23 using namespace swift::misc::network;
24 using namespace swift::misc::physical_quantities;
25 
26 SWIFT_DEFINE_VALUEOBJECT_MIXINS(swift::misc::aviation, CFlightPlanRemarks)
27 SWIFT_DEFINE_VALUEOBJECT_MIXINS(swift::misc::aviation, CFlightPlan)
28 
29 namespace swift::misc::aviation
30 {
31  CFlightPlanRemarks::CFlightPlanRemarks() {}
32 
33  CFlightPlanRemarks::CFlightPlanRemarks(const QString &remarks, bool parse) : m_remarks(cleanRemarks(remarks))
34  {
35  if (parse) { this->parseFlightPlanRemarks(); }
36  }
37 
38  CFlightPlanRemarks::CFlightPlanRemarks(const QString &remarks, CVoiceCapabilities voiceCapabilities, bool parse)
39  : m_remarks(cleanRemarks(remarks)), m_voiceCapabilities(voiceCapabilities)
40  {
41  if (parse) { this->parseFlightPlanRemarks(); }
42  }
43 
44  bool CFlightPlanRemarks::setSelcalCode(const QString &selcal)
45  {
46  if (m_selcalCode == selcal || selcal.length() != 4) { return false; }
47  const QString r =
48  CFlightPlanRemarks::replaceRemark(m_remarks, QStringLiteral("SEL/"), QStringLiteral("SEL/%1").arg(selcal));
49  m_remarks = r;
50  return true;
51  }
52 
54  {
55  m_voiceCapabilities = capabilities;
56  const QString r =
57  CFlightPlanRemarks::replaceVoiceCapabilities(m_voiceCapabilities.toFlightPlanRemarks(), m_remarks);
58  m_remarks = r;
59  }
60 
62  {
63  if (!m_isParsed) { return false; }
64  return this->hasParsedAirlineRemarks() || m_selcalCode.isValid() || !m_voiceCapabilities.isUnknown();
65  }
66 
68  {
69  if (!m_isParsed) { return false; }
70  return !m_radioTelephony.isEmpty() || !m_flightOperator.isEmpty() || m_airlineIcao.hasValidDesignator();
71  }
72 
73  QString CFlightPlanRemarks::convertToQString(bool i18n) const
74  {
75  const QString s = (m_registration.isEmpty() ? QString() : u"reg.: " % m_registration.toQString(i18n)) %
76  (!this->hasValidAirlineIcao() ? QString() : u" airline: " % m_airlineIcao.getDesignator()) %
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: " %
80  m_voiceCapabilities.toQString(i18n);
81  return s.simplified().trimmed();
82  }
83 
85  {
86  const CVoiceCapabilities vc = CVoiceCapabilities::fromText(text);
87  return vc.toFlightPlanRemarks();
88  }
89 
90  QString CFlightPlanRemarks::replaceVoiceCapabilities(const QString &newCapRemarks, const QString &oldRemarks)
91  {
92  if (newCapRemarks.isEmpty()) { return oldRemarks; }
93  if (oldRemarks.isEmpty()) { return newCapRemarks; }
94 
95  QString r(oldRemarks);
96  if (r.contains("/V/", Qt::CaseInsensitive))
97  {
98  r.replace("/V/", newCapRemarks, Qt::CaseInsensitive);
99  return r;
100  }
101  if (r.contains("/R/", Qt::CaseInsensitive))
102  {
103  r.replace("/R/", newCapRemarks, Qt::CaseInsensitive);
104  return r;
105  }
106  if (r.contains("/T/", Qt::CaseInsensitive))
107  {
108  r.replace("/T/", newCapRemarks, Qt::CaseInsensitive);
109  return r;
110  }
111  return newCapRemarks % u' ' % r;
112  }
113 
114  QString CFlightPlanRemarks::cleanRemarks(const QString &remarksIn)
115  {
116  QString r = remarksIn;
117  r.replace(':', ' ');
119  return r;
120  }
121 
123  {
124  // examples: VFPS = VATSIM Flightplan Prefile System
125  // 1) RT/KESTREL OPR/MYTRAVEL REG/G-DAJC SEL/FP-ES PER/C NAV/RNP10
126  // 2) OPR/UAL CALLSIGN/UNITED
127  // 3) /v/FPL-VIR9-IS-A346/DEP/S-EGLL/ARR/KJFK/REG/G-VGAS/TCAS RVR/200 OPR/VIRGIN AIRLINES
128 
129  if (!force && m_isParsed) { return; }
130  m_isParsed = true;
131  if (m_remarks.isEmpty()) { return; }
132  const QString remarks = m_remarks.toUpper();
133  const QString callsign =
134  CCallsign::unifyCallsign(this->getRemark(remarks, "REG/")); // registration is a callsign
135  if (CCallsign::isValidAircraftCallsign(callsign)) { m_registration = CCallsign(callsign, CCallsign::Aircraft); }
136  m_voiceCapabilities = m_voiceCapabilities.isUnknown() ? CVoiceCapabilities(m_remarks) : m_voiceCapabilities;
137  m_flightOperator =
138  this->getRemark(remarks, "OPR/"); // operator, e.g. British airways, sometimes people use ICAO code here
139  m_selcalCode = CSelcal(this->getRemark(remarks, "SEL/"));
140  m_radioTelephony = getRemark(remarks, "CALLSIGN/"); // used similar to radio telephony
141  if (m_radioTelephony.isEmpty()) { m_radioTelephony = getRemark(remarks, "RT/"); }
142  if (!m_flightOperator.isEmpty() && CAirlineIcaoCode::isValidAirlineDesignator(m_flightOperator))
143  {
144  // if people use ICAO code as flight operator swap with airline ICAO
145  m_airlineIcao = CAirlineIcaoCode(m_flightOperator);
146  m_flightOperator.clear();
147  }
148  }
149 
150  QString CFlightPlanRemarks::getRemark(const QString &remarks, const QString &marker)
151  {
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 {}; }
157 
158  // the remarks are poorly formatted:
159  // 1) sometimes the values are enclosed in "/", like "/REG/D-AMBZ/"
160  // 2) sometimes the values are containing space, like "/OPR/DELTA AIRLINES"
161  // 3) in many cases the end delimiter is a new marker or the EOL (otherwise 1 applies)
162 
163  thread_local const QRegularExpression nextMarker("\\s+\\w*/|$");
164  int to1 = remarks.indexOf(nextMarker, f + 1); // for case 2,3
165  if (to1 < 0) { to1 = maxIndex + 1; }
166  int to2 = remarks.indexOf('/', f + 1); // for case 1
167  if (to2 < 0) { to2 = maxIndex + 1; } // no more end markers, ends after last character
168  const int to = qMin(to1, to2);
169  const QString cut = remarks.mid(f, to - f).simplified();
170  return cut;
171  }
172 
173  QString CFlightPlanRemarks::replaceRemark(const QString &remarks, const QString &marker, const QString &newRemark)
174  {
175  QString r(remarks);
176  const int maxIndex = remarks.size() - 1;
177  int f = remarks.indexOf(marker);
178  if (f >= 0)
179  {
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); // for case 2,3
184  if (to1 < 0) { to1 = maxIndex + 1; }
185  int to2 = remarks.indexOf('/', f + 1); // for case 1
186  if (to2 < 0) { to2 = maxIndex + 1; } // no more end markers, ends after last character
187  const int to = qMin(to1, to2);
188  r.remove(f, to - f);
189  }
190  return r.isEmpty() ? newRemark : r % u" " % newRemark;
191  }
192 
193  const QStringList &CFlightPlan::getLogCategories()
194  {
195  static const QStringList cats { CLogCategories::flightPlan() };
196  return cats;
197  }
198 
199  CFlightPlan::CFlightPlan(const CCallsign &callsign, const CFlightPlanAircraftInfo &aircraftInfo,
200  const CAirportIcaoCode &originAirportIcao, const CAirportIcaoCode &destinationAirportIcao,
201  const CAirportIcaoCode &alternateAirportIcao, const QDateTime &takeoffTimePlanned,
202  const QDateTime &takeoffTimeActual, const physical_quantities::CTime &enrouteTime,
203  const physical_quantities::CTime &fuelTime, const CAltitude &cruiseAltitude,
204  const physical_quantities::CSpeed &cruiseTrueAirspeed,
205  CFlightPlan::FlightRules flightRules, const QString &route, const QString &remarks)
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())
212  {
213  m_callsign.setTypeHint(CCallsign::Aircraft);
216  }
217 
218  void CFlightPlan::setCallsign(const CCallsign &callsign)
219  {
220  m_callsign = callsign;
221  m_callsign.setTypeHint(CCallsign::Aircraft);
222  }
223 
224  void CFlightPlan::setAircraftInfo(const CFlightPlanAircraftInfo &aircraftInfo) { m_aircraftInfo = aircraftInfo; }
225 
226  void CFlightPlan::setTakeoffTimePlanned(const QDateTime &takeoffTimePlanned)
227  {
228  m_takeoffTimePlanned = takeoffTimePlanned.toUTC();
229  }
230 
231  void CFlightPlan::setTakeoffTimeActual(const QDateTime &takeoffTimeActual)
232  {
233  m_takeoffTimeActual = takeoffTimeActual.toUTC();
234  }
235 
236  void CFlightPlan::setFlightRule(const QString &flightRule)
237  {
239  this->setFlightRule(r);
240  }
241 
242  void CFlightPlan::setRoute(const QString &route)
243  {
244  QString r = route;
245  r.replace(':', ' ');
246  m_route = asciiOnlyString(r).left(MaxRouteLength).toUpper();
247  }
248 
249  void CFlightPlan::setRemarks(const QString &remarks) { m_remarks = CFlightPlanRemarks(remarks, true); }
250 
251  void CFlightPlan::setVoiceCapabilities(const QString &capabilities)
252  {
253  const CVoiceCapabilities vc = CVoiceCapabilities::fromText(capabilities);
254  m_remarks.setVoiceCapabilities(vc);
255  }
256 
257  QString CFlightPlan::getTakeoffTimePlannedHourMin() const { return m_takeoffTimePlanned.toString("hh:mm"); }
258 
259  QString CFlightPlan::getTakeoffTimeActualHourMin() const { return m_takeoffTimeActual.toString("hh:mm"); }
260 
262  {
263  if (index.isMyself()) { return QVariant::fromValue(*this); }
265 
266  const ColumnIndex i = index.frontCasted<ColumnIndex>();
267  switch (i)
268  {
269  case IndexAlternateAirportIcao: return m_alternateAirportIcao.propertyByIndex(index.copyFrontRemoved());
270  case IndexDestinationAirportIcao: return m_destinationAirportIcao.propertyByIndex(index.copyFrontRemoved());
271  case IndexOriginAirportIcao: return m_originAirportIcao.propertyByIndex(index.copyFrontRemoved());
272  case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved());
273  case IndexRemarks: return QVariant::fromValue(m_remarks);
274  default: return CValueObject::propertyByIndex(index);
275  }
276  }
277 
278  void CFlightPlan::setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
279  {
280  if (index.isMyself())
281  {
282  (*this) = variant.value<CFlightPlan>();
283  return;
284  }
286  {
287  ITimestampBased::setPropertyByIndex(index, variant);
288  return;
289  }
290 
291  const ColumnIndex i = index.frontCasted<ColumnIndex>();
292  switch (i)
293  {
294  case IndexAlternateAirportIcao:
295  m_alternateAirportIcao.setPropertyByIndex(index.copyFrontRemoved(), variant);
296  break;
297  case IndexDestinationAirportIcao:
298  m_destinationAirportIcao.setPropertyByIndex(index.copyFrontRemoved(), variant);
299  break;
300  case IndexOriginAirportIcao: m_originAirportIcao.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
301  case IndexCallsign: m_callsign.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
302  case IndexRemarks: this->setRemarks(variant.toString()); break;
303  default: CValueObject::setPropertyByIndex(index, variant); break;
304  }
305  }
306 
307  QString CFlightPlan::convertToQString(bool i18n) const { return this->buildString(i18n, " "); }
308 
309  QString CFlightPlan::asHTML(bool i18n) const { return this->buildString(i18n, "<br>"); }
310 
311  QString CFlightPlan::buildString(bool i18n, const QString &separator) const
312  {
313  const QString s =
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: " %
321  this->getRemarks();
322  return s;
323  }
324 
326  {
327  if (sbData.isEmpty()) { return CFlightPlan(); }
328  CFlightPlan fp;
329  const QMap<QString, QString> values = parseIniValues(sbData);
330  const QString altStr = values.value("Altitude");
331  const CAltitude alt(altStr.length() < 4 ? "FL" + altStr : altStr + "ft");
332 
333  const QString type = values.value("Type"); // IFR/VFR
334  fp.setFlightRule(type == "0" ? IFR : VFR);
335 
336  const int fuelMins = values.value("FuelMinutes").toInt() + 60 * values.value("FuelHours").toInt();
337  const CTime fuelTime(fuelMins, CTimeUnit::min());
338 
339  const int enrouteMins = values.value("FlightMinutes").toInt() + 60 * values.value("FlightHours").toInt();
340  const CTime enrouteTime(enrouteMins, CTimeUnit::min());
341 
342  fp.setOriginAirportIcao(values.value("Departure"));
343  fp.setDestinationAirportIcao(values.value("Arrival"));
344  fp.setAlternateAirportIcao(values.value("Alternate"));
345  fp.setRemarks(values.value("Remarks"));
346  fp.setRoute(values.value("Route"));
347 
348  fp.setCruiseAltitude(alt);
349  fp.setFuelTime(fuelTime);
350  fp.setEnrouteTime(enrouteTime);
351 
352  fp.setTakeoffTimePlanned(QDateTime::currentDateTimeUtc());
353 
354  int airspeedKts = values.value("Airspeed").toInt();
355  const CSpeed airspeed(airspeedKts, CSpeedUnit::kts());
356  fp.setCruiseTrueAirspeed(airspeed);
357 
358  // Ignoring Heavy flag
359 
360  return fp;
361  }
362 
364  {
365  if (simBrief.isEmpty()) { return CFlightPlan(); }
366  CFlightPlan fp;
367  QDomDocument doc;
368  doc.setContent(simBrief);
369  const QDomNodeList originList = doc.elementsByTagName("origin");
370  if (!originList.isEmpty())
371  {
372  const QDomNode origin = originList.at(0);
373  const QString icao = origin.firstChildElement("icao_code").text();
374  fp.setOriginAirportIcao(icao);
375  }
376  const QDomNodeList destList = doc.elementsByTagName("destination");
377  if (!destList.isEmpty())
378  {
379  const QDomNode dest = destList.at(0);
380  const QString icao = dest.firstChildElement("icao_code").text();
381  fp.setDestinationAirportIcao(icao);
382  }
383  const QDomNodeList altList = doc.elementsByTagName("alternate");
384  if (!altList.isEmpty())
385  {
386  const QDomNode alternate = altList.at(0);
387  const QString icao = alternate.firstChildElement("icao_code").text();
388  fp.setAlternateAirportIcao(icao);
389  }
390  const QDomNodeList generalList = doc.elementsByTagName("general");
391  if (!generalList.isEmpty())
392  {
393  bool ok;
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();
399  fp.setCallsign(CCallsign(airline + flightNumber, CCallsign::Aircraft));
400  const QString cruiseAlt = general.firstChildElement("initial_altitude").text();
401  const int cruiseAltFt = cruiseAlt.toInt(&ok);
402  if (ok)
403  {
404  CAltitude ca(cruiseAltFt, CAltitude::MeanSeaLevel, CLengthUnit::ft());
405  if (cruiseAlt.endsWith("00") && cruiseAltFt > 5000) { ca.toFlightLevel(); }
406  fp.setCruiseAltitude(ca);
407  if (cruiseAltFt >= 10000) { fp.setFlightRule(CFlightPlan::IFR); } // good guess
408  else { fp.setFlightRule(CFlightPlan::VFR); }
409  }
410  else { fp.setCruiseAltitudeString(cruiseAlt); }
411  const QString tas = general.firstChildElement("cruise_tas").text();
412  const int tasKts = tas.toInt(&ok);
413  if (ok) { fp.setCruiseTrueAirspeed(CSpeed(tasKts, CSpeedUnit::kts())); }
414  }
415 
416  const QDomNodeList timeList = doc.elementsByTagName("times");
417  if (!timeList.isEmpty())
418  {
419  bool ok;
420  const QDomNode times = timeList.at(0);
421  const QString enroute = times.firstChildElement("est_time_enroute").text();
422  const int enrouteSecs = enroute.toInt(&ok);
423  if (ok) { fp.setEnrouteTime(CTime(enrouteSecs, CTimeUnit::s())); }
424  const QString endurance = times.firstChildElement("endurance").text();
425  const int enduranceSecs = endurance.toInt(&ok);
426  if (ok) { fp.setFuelTime(CTime(enduranceSecs, CTimeUnit::s())); }
427  const QString depTime = times.firstChildElement("sched_out").text();
428  const int depTimeUnixTs = depTime.toInt(&ok);
429  if (ok)
430  {
431  QDateTime depTs = QDateTime::fromSecsSinceEpoch(depTimeUnixTs, QTimeZone::utc());
432  depTs.setTimeZone(QTimeZone::utc());
433  fp.setTakeoffTimePlanned(depTs);
434  }
435  }
436 
437  const QDomNodeList aircraftList = doc.elementsByTagName("aircraft");
438  if (!aircraftList.isEmpty())
439  {
440  const QDomNode aircraft = aircraftList.at(0);
441  const QString equipment = aircraft.firstChildElement("equip").text();
442  // H-SDE2E3GHIJ1J3J4J5LM1ORWXY/LB1D1
443  const int b = equipment.indexOf('-');
444  const int e = equipment.indexOf('/');
445 
446  if (e > b && e >= 0 && b >= 0 && equipment.size() > e)
447  {
448  // Do not read aircraft ICAO code as this is read/overwritten from the simulator
449 
450  const QChar wtcChar = equipment.mid(0, 1).at(0);
451  const CWakeTurbulenceCategory wtc(wtcChar);
452 
453  const QString comNavEquipmentString = equipment.mid(b + 1, e - b - 1);
454  const CComNavEquipment comNavEquipment(comNavEquipmentString);
455 
456  const QString ssrEquipmentString = equipment.mid(e + 1);
457  const CSsrEquipment ssrEquipment(ssrEquipmentString);
458 
459  const CFlightPlanAircraftInfo info(fp.getAircraftInfo().getAircraftIcao(), comNavEquipment,
460  ssrEquipment, wtc);
461  fp.setAircraftInfo(info);
462  }
463 
465  bool remarksChanged = false;
466  const QString selcal = aircraft.firstChildElement("selcal").text();
467  if (selcal.length() == 4)
468  {
469  const bool c = r.setSelcalCode(selcal);
470  remarksChanged = c || remarksChanged;
471  }
472 
473  if (remarksChanged) { fp.setFlightPlanRemarks(r); }
474  }
475 
476  return fp;
477  }
478 
479  CFlightPlan CFlightPlan::fromMultipleFormats(const QString &data, const QString &fileSuffix)
480  {
481  if (data.isEmpty()) { return CFlightPlan(); }
482  if (fileSuffix.contains("xml", Qt::CaseInsensitive))
483  {
484  if (data.contains("<OFP>", Qt::CaseInsensitive) && data.contains("<general>", Qt::CaseInsensitive))
485  {
486  return CFlightPlan::fromSimBriefFormat(data);
487  }
488  }
489 
490  if (data.contains("[SBFlightPlan]", Qt::CaseInsensitive)) { return CFlightPlan::fromSB4Format(data); }
491  return CFlightPlan::fromJson(data);
492  }
493 
495  {
496  try
497  {
498  QFileInfo fi(fileName);
499  if (fileName.isEmpty())
500  {
501  if (msgs)
502  {
503  msgs->push_back(
504  CStatusMessage(static_cast<CFlightPlan *>(nullptr)).validationError(u"No file name"));
505  }
506  return CFlightPlan();
507  }
508  else
509  {
510  if (!fi.exists())
511  {
512  if (msgs)
513  {
514  msgs->push_back(CStatusMessage(static_cast<CFlightPlan *>(nullptr))
515  .validationError(u"File '%1' does not exist")
516  << fileName);
517  }
518  return CFlightPlan();
519  }
520  }
521 
522  const QString data = CFileUtils::readFileToString(fileName);
523  if (data.isEmpty())
524  {
525  if (msgs)
526  {
527  msgs->push_back(CStatusMessage(static_cast<CFlightPlan *>(nullptr))
528  .validationError(u"File '%1' does not contain data")
529  << fileName);
530  }
531  return CFlightPlan();
532  }
533 
534  if (fileName.endsWith(".sfp", Qt::CaseInsensitive)) { return CFlightPlan::fromSB4Format(data); }
535  if (fileName.endsWith(".json", Qt::CaseInsensitive))
536  {
537  do {
538  CStatusMessage m;
539  if (!json::looksLikeSwiftJson(data))
540  {
541  m = CStatusMessage(static_cast<CFlightPlan *>(nullptr), CStatusMessage::SeverityWarning,
542  u"Reading '%1' yields no data", true)
543  << fileName;
544  if (msgs) { msgs->push_back(m); }
545  break;
546  }
547 
548  try
549  {
550  const QJsonObject jsonObject = json::jsonObjectFromString(data);
551  if (json::looksLikeSwiftTypeValuePairJson(jsonObject))
552  {
553  // CVariant format
554  CVariant variant;
555  variant.convertFromJson(jsonObject);
556  if (variant.canConvert<CFlightPlan>())
557  {
558  const CFlightPlan fp = variant.value<CFlightPlan>();
559  return fp;
560  }
561  else
562  {
563  m = CStatusMessage(static_cast<CFlightPlan *>(nullptr), CStatusMessage::SeverityWarning,
564  u"Wrong format for flight plan in '%1'")
565  << fileName;
566  if (msgs) { msgs->push_back(m); }
567  }
568  }
569  else
570  {
571  const CFlightPlan fp = CFlightPlan::fromJson(jsonObject);
572  return fp;
573  }
574  }
575  catch (const CJsonException &ex)
576  {
577  m = CStatusMessage::fromJsonException(ex, static_cast<CFlightPlan *>(nullptr),
578  "Parse error in " + fileName);
579  if (msgs) { msgs->push_back(m); }
580  break;
581  }
582  }
583  while (false);
584  }
585 
586  return CFlightPlan::fromMultipleFormats(data, fi.suffix());
587  }
588  catch (const CJsonException &ex)
589  {
590  if (msgs)
591  {
593  ex, static_cast<CFlightPlan *>(nullptr),
594  QStringLiteral("Parsing flight plan from '%1' failed.").arg(fileName)));
595  }
596  }
597  return CFlightPlan();
598  }
599 
601  {
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("???");
607 
608  switch (rules)
609  {
610  case VFR: return v;
611  case IFR: return i;
612  case SVFR: return s;
613  case DVFR: return d;
614  case UNKNOWN:
615  default: break;
616  }
617  return unknown;
618  }
619 
621  {
622  if (flightRules.length() < 3) { return UNKNOWN; }
623  const QString fr(flightRules.toUpper().trimmed());
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; }
628  return UNKNOWN;
629  }
630 
631  const QStringList &CFlightPlan::flightRules()
632  {
633  static const QStringList r({ "IFR", "VFR", "SVFR", "DVFR" });
634  return r;
635  }
636 
638  {
639  return rule == CFlightPlan::VFR || rule == CFlightPlan::DVFR || rule == CFlightPlan::SVFR;
640  }
641 
642  bool CFlightPlan::isVFRRules(const QString &rule)
643  {
645  return CFlightPlan::isVFRRules(r);
646  }
647 
649 
650  bool CFlightPlan::isIFRRules(const QString &rule)
651  {
653  return CFlightPlan::isIFRRules(r);
654  }
655 
656  CIcons::IconIndex CFlightPlan::toIcon() const { return CIcons::StandardIconAppFlightPlan16; }
657 
658 } // namespace swift::misc::aviation
static QString readFileToString(const QString &fileNameAndPath)
Read file into string.
Definition: fileutils.cpp:68
IconIndex
Index for each icon, allows to send them via DBus, efficiently store them, etc.
Definition: icons.h:32
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
static const QString & flightPlan()
Flight plan.
Definition: logcategories.h:80
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.
Definition: sequence.h:305
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...
Definition: variant.h:66
T value() const
Return the value converted to the type T.
Definition: variant.h:169
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.
Definition: altitude.h:52
bool toFlightLevel()
MSL to flightlevel.
Definition: altitude.cpp:105
Value object encapsulating information of a callsign.
Definition: callsign.h:30
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: callsign.cpp:310
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: callsign.cpp:324
static QString unifyCallsign(const QString &callsign, TypeHint hint=NoHint)
Unify the callsign by removing illegal characters.
Definition: callsign.cpp:67
bool isEmpty() const
Is empty?
Definition: callsign.h:63
static bool isValidAircraftCallsign(const QString &callsign)
Valid callsign?
Definition: callsign.cpp:369
void setTypeHint(TypeHint hint)
Type hint.
Definition: callsign.h:114
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.
Definition: flightplan.h:148
QString asHTML(bool i18n=false) const
As HTML.
Definition: flightplan.cpp:309
static bool isVFRRules(FlightRules rule)
Is rule a VFR rule?
Definition: flightplan.cpp:637
void setAircraftInfo(const CFlightPlanAircraftInfo &aircraftInfo)
Set information about the aircraft used in this flightplan.
Definition: flightplan.cpp:224
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
Definition: flightplan.cpp:656
void setFlightPlanRemarks(const CFlightPlanRemarks &remarks)
Set FP remarks.
Definition: flightplan.h:381
CFlightPlan()=default
Default constructor.
void setFlightRule(FlightRules flightRule)
Set flight rules (VFR or IFR)
Definition: flightplan.h:285
static FlightRules stringToFlightRules(const QString &flightRules)
String to flight rules.
Definition: flightplan.cpp:620
QString getTakeoffTimePlannedHourMin() const
Get planned takeoff time (planned)
Definition: flightplan.cpp:257
const CFlightPlanRemarks & getFlightPlanRemarks() const
Get the parsable remarks.
Definition: flightplan.h:378
ColumnIndex
Properties by index.
Definition: flightplan.h:165
void setVoiceCapabilities(const QString &capabilities)
Set voice capabilities.
Definition: flightplan.cpp:251
void setOriginAirportIcao(const QString &originAirportIcao)
Set origin airport ICAO code.
Definition: flightplan.h:199
void setTakeoffTimePlanned(const QDateTime &takeoffTimePlanned)
Set planned takeoff time.
Definition: flightplan.cpp:226
void setCruiseTrueAirspeed(const physical_quantities::CSpeed &cruiseTrueAirspeed)
Set planned cruise TAS.
Definition: flightplan.h:279
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: flightplan.cpp:261
void setAlternateAirportIcao(const QString &alternateAirportIcao)
Set alternate destination airport ICAO code.
Definition: flightplan.h:220
void setFuelTime(const physical_quantities::CTime &fuelTime)
Set amount of fuel load in time.
Definition: flightplan.h:259
static const QStringList & flightRules()
All rules as string.
Definition: flightplan.cpp:631
static CFlightPlan fromSimBriefFormat(const QString &simBrief)
From SimBrief format (XML)
Definition: flightplan.cpp:363
FlightRules
Flight rules (VFR or IFR)
Definition: flightplan.h:155
@ IFR
Instrument flight rules.
Definition: flightplan.h:157
@ VFR
Visual flight rules.
Definition: flightplan.h:156
@ SVFR
Special VFR (reserved for ATC use),.
Definition: flightplan.h:158
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: flightplan.cpp:278
static CFlightPlan fromMultipleFormats(const QString &data, const QString &fileSuffix)
From multiple formats.
Definition: flightplan.cpp:479
static const QString & flightRulesToString(FlightRules rules)
Rules to string.
Definition: flightplan.cpp:600
void setTakeoffTimeActual(const QDateTime &takeoffTimeActual)
Set actual takeoff time (reserved for ATC use)
Definition: flightplan.cpp:231
const QString & getRemarks() const
Get remarks string.
Definition: flightplan.h:375
void setCruiseAltitudeString(const QString &altitudeString)
Cruising altitude already as string.
Definition: flightplan.h:276
QString getTakeoffTimeActualHourMin() const
Get actual takeoff time (actual)
Definition: flightplan.cpp:259
void setRemarks(const QString &remarks)
Set remarks string (max 100 characters)
Definition: flightplan.cpp:249
static const QStringList & getLogCategories()
The log. catgeories.
Definition: flightplan.cpp:193
QString convertToQString(bool i18n=false) const
Cast as QString.
Definition: flightplan.cpp:307
void setEnrouteTime(const physical_quantities::CTime &enrouteTime)
Set planned enroute flight time.
Definition: flightplan.h:252
static CFlightPlan fromSB4Format(const QString &sbData)
From SB4 data.
Definition: flightplan.cpp:325
void setRoute(const QString &route)
Set route string.
Definition: flightplan.cpp:242
static bool isIFRRules(FlightRules rule)
Is rule a IFR rule?
Definition: flightplan.cpp:648
static constexpr int MaxRouteLength
Max.route length.
Definition: flightplan.h:177
void setDestinationAirportIcao(const QString &destinationAirportIcao)
Set destination airport ICAO code.
Definition: flightplan.h:208
void setCruiseAltitude(const CAltitude &cruiseAltitude)
Set planned cruise altitude.
Definition: flightplan.h:269
CFlightPlanAircraftInfo getAircraftInfo() const
Get ICAO aircraft NAV/COM equipment.
Definition: flightplan.h:384
static CFlightPlan loadFromMultipleFormats(const QString &fileName, CStatusMessageList *msgs=nullptr)
Load from multiple formats.
Definition: flightplan.cpp:494
void setCallsign(const CCallsign &callsign)
Callsign (of aircraft)
Definition: flightplan.cpp:218
Flight plan remarks, parsed values.
Definition: flightplan.h:46
bool hasValidAirlineIcao() const
Valid airline ICAO?
Definition: flightplan.h:98
bool setSelcalCode(const QString &selcal)
SELCAL code.
Definition: flightplan.cpp:44
static QString textToVoiceCapabilitiesRemarks(const QString &text)
Turn text into voice capabilities for remarks.
Definition: flightplan.cpp:84
bool hasAnyParsedRemarks() const
Any remarks available?
Definition: flightplan.cpp:61
QString convertToQString(bool i18n=false) const
Cast as QString.
Definition: flightplan.cpp:73
static QString cleanRemarks(const QString &remarksIn)
Clean up remarks string.
Definition: flightplan.cpp:114
bool hasParsedAirlineRemarks() const
Airline remarks.
Definition: flightplan.cpp:67
static QString replaceVoiceCapabilities(const QString &newCapRemarks, const QString &oldRemarks)
Replace the voice capabilities remarks part.
Definition: flightplan.cpp:90
void setVoiceCapabilities(const network::CVoiceCapabilities &capabilities)
Set voice capabilities.
Definition: flightplan.cpp:53
void parseFlightPlanRemarks(bool force=false)
Parse remarks from a flight plan.
Definition: flightplan.cpp:122
Value object for SELCAL.
Definition: selcal.h:31
bool isValid() const
Is valid?
Definition: selcal.h:43
const QString & getCode() const
Get SELCAL code.
Definition: selcal.h:46
ICAO flightplan field 10b.
Definition: ssrequipment.h:16
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: mixinindex.h:160
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: mixinindex.h:167
static DerivedObj fromJson(const QJsonObject &json)
Get object from QJsonObject.
Definition: mixinjson.h:190
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
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.
Definition: units.h:1007
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
Definition: json.cpp:413
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.
Definition: stringutils.h:204
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
Definition: valueobject.h:67