swift
aircraftmodel.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 <QFileInfo>
7 #include <QJsonDocument>
8 #include <QJsonObject>
9 #include <QJsonValue>
10 #include <QList>
11 #include <QString>
12 #include <QStringBuilder>
13 #include <QtGlobal>
14 
15 #include "config/buildconfig.h"
16 #include "misc/comparefunctions.h"
18 #include "misc/fileutils.h"
19 #include "misc/logcategories.h"
22 #include "misc/statusmessage.h"
23 #include "misc/stringutils.h"
24 #include "misc/verify.h"
25 
26 using namespace swift::config;
27 using namespace swift::misc::aviation;
28 using namespace swift::misc::physical_quantities;
29 using namespace swift::misc::db;
30 
31 SWIFT_DEFINE_VALUEOBJECT_MIXINS(swift::misc::simulation, CAircraftModel)
32 
33 namespace swift::misc::simulation
34 {
35  void CAircraftModel::registerMetadata()
36  {
38  qRegisterMetaType<ModelType>();
39  }
40 
41  CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type)
42  : m_modelString(model.trimmed().toUpper()), m_modelType(type)
43  {}
44 
46  const CLivery &livery)
47  : m_aircraftIcao(icao), m_livery(livery), m_modelString(model.trimmed().toUpper()), m_modelType(type)
48  {}
49 
51  const CAircraftIcaoCode &icao, const aviation::CLivery &livery)
52  : m_aircraftIcao(icao), m_livery(livery), m_modelString(model.trimmed().toUpper()),
53  m_description(description.trimmed()), m_modelType(type)
54  {}
55 
57  const CSimulatorInfo &simulator, const QString &name, const QString &description)
58  : m_simulator(simulator), m_modelString(model.trimmed().toUpper()), m_name(name.trimmed()),
59  m_description(description.trimmed()), m_modelType(type)
60  {}
61 
63  const CSimulatorInfo &simulator, const QString &name, const QString &description,
64  const CAircraftIcaoCode &icao, const CLivery &livery)
65  : m_aircraftIcao(icao), m_livery(livery), m_simulator(simulator), m_modelString(model.trimmed().toUpper()),
66  m_name(name.trimmed()), m_description(description.trimmed()), m_modelType(type)
67  {}
68 
70  {
71  const QString s = (this->hasAnyModelString() ?
72  inApostrophes(this->getAllModelStringsAliasesAndDbKey(), true) % QStringLiteral(" ") :
73  QString()) %
74  u" type: '" % this->getModelTypeAsString() % u"' ICAO: {" %
75  this->getAircraftIcaoCode().toQString(i18n) % u"} CG: " %
76  this->getCG().valueRoundedWithUnit(1) % u"' livery: {" % m_livery.toQString(i18n) %
77  u"} file: '" % m_fileName % u'\'';
78  return s;
79  }
80 
82  {
83  QJsonObject obj;
84 
85  // filename not in DB
86  obj.insert("id", this->getDbKeyAsJsonValue());
87  obj.insert("name", this->getName());
88  obj.insert("modelstring", QJsonValue(m_modelString));
89  obj.insert("description", QJsonValue(m_description));
90  obj.insert("parts", QJsonValue(m_supportedParts));
91  obj.insert("version", CBuildConfig::getVersionString());
92  obj.insert("mode", QJsonValue(getModelModeAsString().left(1).toUpper())); // clazy:exclude=qstring-left
93  if (m_cg.isNull())
94  {
95  obj.insert("cgft", QJsonValue()); // null value
96  }
97  else { obj.insert("cgft", QJsonValue(m_cg.value(CLengthUnit::ft()))); }
98 
99  // sims
100  const CSimulatorInfo sim(getSimulator());
101  QString flag = CDatastoreUtility::boolToDbYN(sim.isFSX());
102  obj.insert("simfsx", QJsonValue(flag));
103  flag = CDatastoreUtility::boolToDbYN(sim.isP3D());
104  obj.insert("simp3d", QJsonValue(flag));
105  flag = CDatastoreUtility::boolToDbYN(sim.isFS9());
106  obj.insert("simfs9", QJsonValue(flag));
107  flag = CDatastoreUtility::boolToDbYN(sim.isMSFS());
108  obj.insert("simmsfs", QJsonValue(flag));
109  flag = CDatastoreUtility::boolToDbYN(sim.isMSFS2024());
110  obj.insert("simmsfs2024", QJsonValue(flag));
111  flag = CDatastoreUtility::boolToDbYN(sim.isXPlane());
112  obj.insert("simxplane", QJsonValue(flag));
113  flag = CDatastoreUtility::boolToDbYN(sim.isFG());
114  obj.insert("simfg", QJsonValue(flag));
115 
116  // foreign keys
117  obj.insert("iddistributor", this->getDistributor().getDbKeyAsJsonValue());
118  obj.insert("idaircrafticao", this->getAircraftIcaoCode().getDbKeyAsJsonValue());
119  obj.insert("idlivery", this->getLivery().getDbKeyAsJsonValue());
120  obj.insert(
121  "idairlineicao",
122  this->getLivery().getAirlineIcaoCode().getDbKeyAsJsonValue()); // not really needed if livery is complete
123 
124  return obj;
125  }
126 
128  {
129  return QJsonDocument(this->toDatabaseJson()).toJson(format);
130  }
131 
133  {
134  QJsonObject json;
135  // NOLINTBEGIN(readability-static-accessed-through-instance)
136  introspect<CAircraftModel>().forEachMember([&, this](auto member) {
137  if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
138  {
139  auto &&maybeMemo = helper.maybeMemoize(member.in(*this));
140  json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(maybeMemo));
141  }
142  });
143  // NOLINTEND(readability-static-accessed-through-instance)
144  return json;
145  }
146 
148  {
149  // NOLINTBEGIN(readability-static-accessed-through-instance)
150  introspect<CAircraftModel>().forEachMember([&, this](auto member) {
151  if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
152  {
153  auto it = json.find(CExplicitLatin1String(member.latin1Name()));
154  if (it != json.end()) { it.value() >> helper.maybeUnmemoize(member.in(*this)).get(); }
155  }
156  });
157  // NOLINTEND(readability-static-accessed-through-instance)
158  }
159 
161  {
162  return QStringLiteral(
163  "Model: %1 changed: %2%3Simulator: %4 Mode: %5 Distributor: %6%7Aircraft ICAO: %8%9Livery: %10")
164  .arg(this->getModelStringAndDbKey(), this->getFormattedUtcTimestampYmdhms(), separator,
165  this->getSimulator().toQString(true), this->getModelModeAsString(),
166  this->getDistributor().getIdAndDescription(), separator, this->getAircraftIcaoCode().asHtmlSummary(),
167  separator)
168  .arg(this->getLivery().asHtmlSummary("&nbsp;"))
169  .replace(" ", "&nbsp;");
170  }
171 
172  bool CAircraftModel::matchesFileName(const QString &fileName) const
173  {
174  return stringCompare(fileName, m_fileName, CFileUtils::osFileNameCaseSensitivity());
175  }
176 
178  {
179  CStatusMessageList msgs;
180  const ModelType t = this->getModelType();
183  {
184  if (!this->hasExistingCorrespondingFile())
185  {
186  const CStatusMessage m = CStatusMessage(this).validationWarning(u"File '%1' not readable")
187  << this->getFileName();
188  msgs.push_back(m);
189  }
190  else
191  {
192  const CStatusMessage m = CStatusMessage(this).validationInfo(u"File '%1' existing")
193  << this->getFileName();
194  msgs.push_back(m);
195  }
196  }
197  else
198  {
199  const CStatusMessage m = CStatusMessage(this).validationError(u"Invalid model type to check: '%1'")
200  << this->getModelTypeAsString();
201  msgs.push_back(m);
202  }
203  return msgs;
204  }
205 
207  {
208  const bool nw = this->getModelType() == CAircraftModel::TypeQueriedFromNetwork ||
210  this->getModelType() == CAircraftModel::TypeUnknown;
211  return nw;
212  }
213 
215  {
216  m_callsign = callsign;
217  m_callsign.setTypeHint(CCallsign::Aircraft);
218  }
219 
221  {
222  if (this->hasValidDbKey())
223  {
224  return this->hasModelString() ? this->getModelString() % this->getDbKeyAsStringInParentheses(" ") :
225  this->getDbKeyAsString();
226  }
227  else { return this->getModelString(); }
228  }
229 
231  {
232  if (!this->hasModelStringAlias()) { return m_modelString; }
233  if (!this->hasModelString()) { return m_modelStringAlias; }
234  return m_modelString % u", " % m_modelStringAlias;
235  }
236 
238  {
239  if (!this->hasModelStringAlias() || m_modelString.isEmpty()) { return this->getModelStringAndDbKey(); }
240  if (!this->isLoadedFromDb()) { return this->getAllModelStringsAndAliases(); }
241  return this->getAllModelStringsAndAliases() % " " % this->getDbKeyAsStringInParentheses();
242  }
243 
244  bool CAircraftModel::isVtol() const { return this->getAircraftIcaoCode().isVtol(); }
245 
247  {
248  if (index.isMyself()) { return QVariant::fromValue(*this); }
249  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
250  {
251  return IDatastoreObjectWithIntegerKey::propertyByIndex(index);
252  }
253  if (IOrderable::canHandleIndex(index)) { return IOrderable::propertyByIndex(index); }
254 
255  const auto i = index.frontCasted<ColumnIndex>();
256  switch (i)
257  {
258  case IndexModelString: return { m_modelString };
259  case IndexModelStringAlias: return { m_modelStringAlias };
261  case IndexHasQueriedModelString: return QVariant::fromValue(this->hasQueriedModelString());
262  case IndexModelType: return QVariant::fromValue(m_modelType);
263  case IndexModelTypeAsString: return { this->getModelTypeAsString() };
264  case IndexModelMode: return QVariant::fromValue(m_modelMode);
265  case IndexModelModeAsString: return QVariant::fromValue(this->getModelModeAsString());
266  case IndexModelModeAsIcon: return QVariant::fromValue(this->getModelModeAsIcon());
267  case IndexDistributor: return m_distributor.propertyByIndex(index.copyFrontRemoved());
268  case IndexSimulatorInfo: return m_simulator.propertyByIndex(index.copyFrontRemoved());
269  case IndexSimulatorInfoAsString: return { m_simulator.toQString() };
270  case IndexDescription: return { m_description };
271  case IndexName: return { m_name };
272  case IndexFileName: return { m_fileName };
273  case IndexCG: return m_cg.propertyByIndex(index.copyFrontRemoved());
274  case IndexSupportedParts: return { m_supportedParts };
275  case IndexFileTimestamp: return QVariant::fromValue(this->getFileTimestamp());
276  case IndexFileTimestampFormattedYmdhms: return QVariant::fromValue(this->getFormattedFileTimestampYmdhms());
277  case IndexAircraftIcaoCode: return m_aircraftIcao.propertyByIndex(index.copyFrontRemoved());
278  case IndexLivery: return m_livery.propertyByIndex(index.copyFrontRemoved());
279  case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved());
280  case IndexMembersDbStatus: return this->getMembersDbStatus();
281  default: return CValueObject::propertyByIndex(index);
282  }
283  }
284 
286  {
287  if (index.isMyself())
288  {
289  (*this) = variant.value<CAircraftModel>();
290  return;
291  }
292  if (IOrderable::canHandleIndex(index))
293  {
294  IOrderable::setPropertyByIndex(index, variant);
295  return;
296  }
297  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
298  {
299  IDatastoreObjectWithIntegerKey::setPropertyByIndex(index, variant);
300  return;
301  }
302 
303  const auto i = index.frontCasted<ColumnIndex>();
304  switch (i)
305  {
306  case IndexModelString: m_modelString = variant.toString(); break;
307  case IndexModelStringAlias: m_modelStringAlias = variant.toString(); break;
308  case IndexAircraftIcaoCode: m_aircraftIcao.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
309  case IndexLivery: m_livery.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
310  case IndexDistributor: m_distributor.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
311  case IndexDescription: m_description = variant.toString(); break;
312  case IndexSimulatorInfo: m_simulator.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
313  case IndexName: m_name = variant.toString(); break;
314  case IndexCG: m_cg.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
315  case IndexSupportedParts: this->setSupportedParts(variant.toString()); break;
316  case IndexModelType: m_modelType = variant.value<ModelType>(); break;
317  case IndexFileName: m_fileName = variant.toString(); break;
318  case IndexCallsign:
319  m_callsign.setPropertyByIndex(index.copyFrontRemoved(), variant);
320  m_callsign.setTypeHint(CCallsign::Aircraft);
321  break;
322  case IndexFileTimestamp:
323  if (variant.canConvert<QDateTime>()) { this->setFileTimestamp(variant.value<QDateTime>()); }
324  else if (variant.canConvert<qint64>()) { m_fileTimestamp = variant.value<qint64>(); }
325  break;
326  case IndexModelMode:
327  if (static_cast<QMetaType::Type>(variant.typeId()) == QMetaType::QString)
328  {
329  this->setModelModeAsString(variant.toString());
330  }
331  else { m_modelMode = variant.value<ModelMode>(); }
332  break;
333  // no setter indexes ignored
334  case IndexHasQueriedModelString: break;
335  case IndexModelTypeAsString: break;
336  case IndexModelModeAsString: break;
337  case IndexModelModeAsIcon: break;
338  case IndexFileTimestampFormattedYmdhms: break;
339  case IndexSimulatorInfoAsString: break;
340  case IndexMembersDbStatus: break;
341 
342  default: CValueObject::setPropertyByIndex(index, variant); break;
343  }
344  }
345 
347  {
348  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
349  {
350  return IDatastoreObjectWithIntegerKey::comparePropertyByIndex(index, compareValue);
351  }
352  if (IOrderable::canHandleIndex(index)) { return IOrderable::comparePropertyByIndex(index, compareValue); }
353  if (index.isMyself()) { return m_modelString.compare(compareValue.getModelString(), Qt::CaseInsensitive); }
354  const auto i = index.frontCasted<ColumnIndex>();
355  switch (i)
356  {
357  case IndexModelString: return m_modelString.compare(compareValue.getModelString(), Qt::CaseInsensitive);
358  case IndexModelStringAlias:
359  return m_modelStringAlias.compare(compareValue.getModelStringAlias(), Qt::CaseInsensitive);
363  case IndexHasQueriedModelString:
364  return Compare::compare(this->hasQueriedModelString(), compareValue.hasQueriedModelString());
365  case IndexAircraftIcaoCode:
366  return m_aircraftIcao.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getAircraftIcaoCode());
367  case IndexLivery: return m_livery.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getLivery());
368  case IndexDistributor:
369  return m_distributor.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getDistributor());
370  case IndexDescription: return m_description.compare(compareValue.getDescription(), Qt::CaseInsensitive);
371  case IndexName: return m_name.compare(compareValue.getName(), Qt::CaseInsensitive);
372  case IndexCallsign:
373  return m_callsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCallsign());
374  case IndexFileName: return m_fileName.compare(compareValue.getFileName(), Qt::CaseInsensitive);
375  case IndexCG: return m_cg.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCG());
376  case IndexSupportedParts: return m_supportedParts.compare(compareValue.getSupportedParts());
377  case IndexModelTypeAsString:
378  case IndexModelType: return Compare::compare(m_modelType, compareValue.getModelType());
379  case IndexSimulatorInfoAsString:
380  case IndexSimulatorInfo:
381  return m_simulator.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getSimulator());
382  case IndexFileTimestamp:
383  case IndexFileTimestampFormattedYmdhms: return Compare::compare(m_fileTimestamp, compareValue.m_fileTimestamp);
384  case IndexModelMode:
385  case IndexModelModeAsString:
386  case IndexModelModeAsIcon: return Compare::compare(m_modelMode, compareValue.getModelMode());
387  case IndexMembersDbStatus: return getMembersDbStatus().compare(compareValue.getMembersDbStatus());
388  default: break;
389  }
390  Q_ASSERT_X(false, Q_FUNC_INFO, "No comparison");
391  return 0;
392  }
393 
395  {
396  if (m_aircraftIcao == aircraftIcaoCode) { return false; }
397  m_aircraftIcao = aircraftIcaoCode;
398  return true;
399  }
400 
402  {
403  m_aircraftIcao.setDesignator(designator);
404  }
405 
407  const CAirlineIcaoCode &airlineIcaoCode)
408  {
409  m_aircraftIcao = aircraftIcaoCode;
410  m_livery.setAirlineIcaoCode(airlineIcaoCode);
411  }
412 
414  {
415  return this->hasKnownAircraftDesignator() && m_livery.hasValidAirlineDesignator();
416  }
417 
418  bool CAircraftModel::hasAircraftDesignator() const { return m_aircraftIcao.hasDesignator(); }
419 
420  bool CAircraftModel::hasKnownAircraftDesignator() const { return m_aircraftIcao.hasKnownDesignator(); }
421 
422  bool CAircraftModel::hasCategory() const { return m_aircraftIcao.hasCategory(); }
423 
425 
427  {
428  return this->hasAircraftDesignator() && this->hasAirlineDesignator();
429  }
430 
432  {
433  return this->getAircraftIcaoCode().isMilitary() || this->getLivery().isMilitary();
434  }
435 
436  bool CAircraftModel::isCivilian() const { return !this->isMilitary(); }
437 
439  {
440  if (order < 0) { return false; }
441  if (!m_distributor.isLoadedFromDb()) { return false; }
442  m_distributor.setOrder(order);
443  return true;
444  }
445 
447  {
448  if (distributors.isEmpty()) { return false; }
449  bool found = false;
450  const int noDistributorOrder = distributors.size();
451  if (this->hasDbDistributor())
452  {
453  const CDistributor d = distributors.findByKeyOrAlias(m_distributor.getDbKey());
454  if (d.hasValidDbKey())
455  {
456  m_distributor.setOrder(d.getOrder());
457  found = true;
458  }
459  else { m_distributor.setOrder(noDistributorOrder); }
460  }
461  else { m_distributor.setOrder(noDistributorOrder); }
462  return found;
463  }
464 
465  bool CAircraftModel::hasDbDistributor() const { return m_distributor.isLoadedFromDb(); }
466 
468  {
469  return m_distributor.hasValidDbKey(); // key is valid, but not guaranteed from DB
470  }
471 
472  bool CAircraftModel::matchesDbDistributor(const CDistributor &distributor) const
473  {
474  if (!distributor.isLoadedFromDb()) { return false; }
475  if (!this->hasDbDistributor()) { return false; }
476  return m_distributor.getDbKey() == distributor.getDbKey();
477  }
478 
480  {
481  if (distributors.isEmpty()) { return false; }
482  if (!this->hasDbDistributor()) { return false; }
483  return distributors.matchesAnyKeyOrAlias(m_distributor.getDbKey());
484  }
485 
486  void CAircraftModel::setSupportedParts(const QString &supportedParts)
487  {
489  }
490 
491  bool CAircraftModel::matchesMode(ModelModeFilter mode) const
492  {
493  if (mode == All) { return true; }
494  return (mode & m_modelMode) > 0;
495  }
496 
498  {
499  switch (this->getModelMode())
500  {
501  case Include: return CIcon::iconByIndex(CIcons::ModelInclude);
502  case Exclude: return CIcon::iconByIndex(CIcons::ModelExclude);
503  case Undefined: return CIcon::iconByIndex(CIcons::StandardIconUnknown16);
504  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode"); break;
505  }
506  return CIcon::iconByIndex(CIcons::ModelInclude);
507  }
508 
510  {
512  }
513 
515  {
516  return (static_cast<int>(simulator.getSimulator()) & static_cast<int>(this->getSimulator().getSimulator())) > 0;
517  }
518 
519  bool CAircraftModel::matchesSimulatorFlag(CSimulatorInfo::Simulator simulator) const
520  {
521  return (static_cast<int>(simulator) & static_cast<int>(this->getSimulator().getSimulator())) > 0;
522  }
523 
525  {
526  return this->hasValidFileTimestamp() ? QDateTime::fromMSecsSinceEpoch(m_fileTimestamp, QTimeZone::utc()) :
527  QDateTime();
528  }
529 
531  {
532  return this->hasValidFileTimestamp() ? this->getFileTimestamp().toString("yyyy-MM-dd HH:mm:ss") : "";
533  }
534 
535  bool CAircraftModel::hasValidFileTimestamp() const { return m_fileTimestamp >= 0; }
536 
537  void CAircraftModel::setFileTimestamp(const QDateTime &timestampUtc)
538  {
539  m_fileTimestamp = timestampUtc.isValid() ? timestampUtc.toMSecsSinceEpoch() : -1;
540  }
541 
542  void CAircraftModel::setFileTimestamp(qint64 timestamp) { m_fileTimestamp = (timestamp < 0) ? -1 : timestamp; }
543 
545  {
546  this->setFileName(fileInfo.absoluteFilePath());
547  const QDateTime modified = fileInfo.lastModified();
548  if (modified.isValid())
549  {
550  this->setFileTimestamp(modified);
551  this->setUtcTimestamp(modified);
552  }
553  else
554  {
555  const QDateTime created = fileInfo.lastModified();
556  this->setFileTimestamp(created);
557  this->setUtcTimestamp(created);
558  }
559  }
560 
562  {
563  static const QString p("swift_");
564  return p;
565  }
566 
568  {
569  return (liveryString.length() > liveryStringPrefix().length() &&
571  }
572 
573  QString CAircraftModel::getSwiftLiveryString(bool aircraftIcao, bool livery, bool model) const
574  {
575  if (!aircraftIcao && !livery && !model) { return {}; }
576  const QString l =
577  (livery && this->getLivery().hasValidDbKey() ? u'l' % this->getLivery().getDbKeyAsString() : QString()) %
578  (aircraftIcao && this->getAircraftIcaoCode().hasValidDbKey() ?
579  QStringLiteral("a") % this->getAircraftIcaoCode().getDbKeyAsString() :
580  QString()) %
581  (model && this->hasValidDbKey() ? u'm' % this->getDbKeyAsString() : QString());
582 
583  return l.isEmpty() ? QString() : liveryStringPrefix() % l;
584  }
585 
587  {
588  return (sim.isFG()) ? this->getSwiftLiveryString(true, false, false) : this->getSwiftLiveryString();
589  }
590 
592  {
593  // "swift_m22l33a11"
594  if (!CAircraftModel::isSwiftLiveryString(liveryString)) { return {}; }
595 
596  DBTripleIds ids;
597  const QString ls = liveryString.mid(liveryStringPrefix().length()).toLower();
598  for (int c = 0; c < ls.length(); c++)
599  {
600  const QChar m = ls[c];
601  if ((m == 'm' || m == 'a' || m == 'l') && (c + 1) < ls.length())
602  {
603  const int cs = c + 1;
604  int cc = cs;
605  while (cc < ls.length() && ls[cc].isDigit()) { cc++; } // find end of id
606  if (cc > cs)
607  {
608  const QString idString = ls.mid(cs, cc - cs);
609  const int id = idString.toInt();
610  c = cc - 1; // +1 again in for
611 
612  if (m == 'm') { ids.model = id; }
613  else if (m == 'a') { ids.aircraft = id; }
614  else if (m == 'l') { ids.livery = id; }
615  }
616  }
617  }
618  return ids;
619  }
620 
621  void CAircraftModel::updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority)
622  {
623  if (dbModelPriority && !this->hasValidDbKey() && otherModel.hasValidDbKey())
624  {
625  // we have no DB data, but the other one has
626  // so we change roles. We take the DB object as base, and update our parts
627  CAircraftModel copy(otherModel);
628  copy.updateMissingParts(*this);
629  *this = copy;
630  return;
631  }
632 
633  // local file names and file timestamp
634  this->updateLocalFileNames(otherModel);
635  if (m_fileTimestamp < 0 || otherModel.m_fileTimestamp > m_fileTimestamp)
636  {
637  this->setFileTimestamp(otherModel.getFileTimestamp());
638  }
639 
640  // both are DB data, treat as being the same except for filename maybe
641  if (this->hasValidDbKey() && otherModel.hasValidDbKey()) { return; }
642 
643  // update attributes where applicable
644  if (m_callsign.isEmpty()) { this->setCallsign(otherModel.getCallsign()); }
645  if (m_modelString.isEmpty()) { this->setModelString(otherModel.getModelString()); }
646  if (m_name.isEmpty()) { this->setName(otherModel.getName()); }
647  if (m_modelType == TypeUnknown) { m_modelType = otherModel.getModelType(); }
648  if (m_modelMode == Undefined) { m_modelType = otherModel.getModelType(); }
649  if (m_description.isEmpty() || m_description.startsWith(CAircraftModel::autoGenerated(), Qt::CaseInsensitive))
650  {
651  this->setDescription(otherModel.getDescription());
652  }
653  if (this->getSimulator().isUnspecified())
654  {
655  // simulator can only be overridden as simulators can also be removed
656  this->setSimulator(otherModel.getSimulator());
657  }
658 
660  m_livery.updateMissingParts(otherModel.getLivery());
661  m_aircraftIcao.updateMissingParts(otherModel.getAircraftIcaoCode());
662  m_distributor.updateMissingParts(otherModel.getDistributor());
663  }
664 
666  {
667  if (otherModel.hasExistingCorrespondingFile()) { this->setFileName(otherModel.getFileName()); }
668  }
669 
671  {
672  return m_modelType == TypeQueriedFromNetwork && this->hasModelString();
673  }
674 
676  {
677  return m_modelType == TypeManuallySet && this->hasModelString();
678  }
679 
680  bool CAircraftModel::hasDescription(bool ignoreAutoGenerated) const
681  {
682  if (m_description.isEmpty()) { return false; }
683  if (!ignoreAutoGenerated) { return true; }
684  return (!this->getDescription().startsWith(autoGenerated(), Qt::CaseInsensitive));
685  }
686 
687  bool CAircraftModel::hasValidSimulator() const { return m_simulator.isAnySimulator(); }
688 
690  {
691  return (this->isLoadedFromDb() ? QStringLiteral("M") : QStringLiteral("m")) %
692  (this->getDistributor().isLoadedFromDb() ? QStringLiteral("D") : QStringLiteral("d")) %
693  (this->getAircraftIcaoCode().isLoadedFromDb() ? QStringLiteral("A") : QStringLiteral("a")) %
694  (this->getLivery().isLoadedFromDb() && getLivery().isColorLivery() ?
695  QStringLiteral("C-") :
696  (this->getLivery().isLoadedFromDb() ? QStringLiteral("L") : QStringLiteral("l")) %
697  (this->getLivery().getAirlineIcaoCode().isLoadedFromDb() ? QStringLiteral("A") :
698  QStringLiteral("a")));
699  }
700 
702 
704  {
706  {
707  // this is already a local model, ignore
708  return;
709  }
710 
712  {
713  // other local, priority
714  this->setFileName(model.getFileName());
715  return;
716  }
717 
718  // both not local, override empty values
719  if (m_fileName.isEmpty()) { this->setFileName(model.getFileName()); }
720  }
721 
722  bool CAircraftModel::adjustLocalFileNames(const QString &newModelDir, const QString &stripModelDirIndicator)
723  {
724  if (!this->hasFileName()) { return false; }
725  const QString md = CFileUtils::normalizeFilePathToQtStandard(newModelDir);
726  int i = -1;
727  if (stripModelDirIndicator.isEmpty())
728  {
729  QString strip = md.mid(md.lastIndexOf('/'));
730  i = m_fileName.lastIndexOf(strip);
731  }
732  else { i = m_fileName.lastIndexOf(stripModelDirIndicator); }
733  if (i < 0) { return false; }
734  m_fileName = CFileUtils::appendFilePaths(newModelDir, m_fileName.mid(i));
735  return true;
736  }
737 
739  {
740  if (!this->hasFileName()) { return false; }
742  if (!fi.exists()) { return false; }
743  const bool r = fi.isReadable();
744  return r;
745  }
746 
748  {
749  if (!this->hasFileName()) { return {}; }
751  return fi.absoluteDir();
752  }
753 
755  {
756  if (!this->hasFileName()) { return {}; }
757  return this->getFileDirectory().absolutePath();
758  }
759 
761  {
762  const QString p(this->getFileDirectoryPath());
763  if (path.isEmpty() || p.isEmpty()) { return false; }
764  if (path.startsWith('/'))
765  {
766  if (path.endsWith('/')) { return p.contains(path.mid(1, path.length() - 2), cs); }
767  return p.contains(path.mid(1));
768  }
769  if (path.endsWith('/')) { return p.contains(path.left(path.length() - 1), cs); }
770  return (p.contains(path, cs));
771  }
772 
773  bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
774  {
775  return stringCompare(modelString, m_modelString, sensitivity);
776  }
777 
778  bool CAircraftModel::matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
779  {
780  if (this->matchesModelString(modelString, sensitivity)) { return true; }
781  return stringCompare(modelString, m_modelStringAlias, sensitivity);
782  }
783 
784  int CAircraftModel::calculateScore(const CAircraftModel &compareModel, bool preferColorLiveries,
785  CStatusMessageList *log) const
786  {
787  const int icaoScore = this->getAircraftIcaoCode().calculateScore(compareModel.getAircraftIcaoCode(), log);
788  const int liveryScore = this->getLivery().calculateScore(compareModel.getLivery(), preferColorLiveries, log);
789  CCallsign::addLogDetailsToList(
790  log, this->getCallsign(),
791  QStringLiteral("ICAO score: %1 | livery score: %2").arg(icaoScore).arg(liveryScore));
792  return qRound(0.5 * (icaoScore + liveryScore));
793  }
794 
795  CStatusMessageList CAircraftModel::validate(bool withNestedObjects) const
796  {
797  static const CLogCategoryList cats(CLogCategoryList(this).withValidation());
798  CStatusMessageList msgs;
799  if (!hasModelString())
800  {
801  msgs.push_back(
802  CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: missing model string (aka key)"));
803  }
804  if (!hasValidSimulator())
805  {
806  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: no simulator set"));
807  }
808  // as of T34 made description optional, lines can be removed after 6/2017
809  // if (!hasDescription()) {msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, u"Model: no
810  // description")); }
811  if (withNestedObjects)
812  {
813  msgs.push_back(m_aircraftIcao.validate());
814  msgs.push_back(m_livery.validate());
815  msgs.push_back(m_distributor.validate());
816  }
817  return msgs;
818  }
819 
821  CStatusMessage equalMessage(bool same, const CAircraftModel &model, const QString &description,
822  const QString &oldValue, const QString &newValue)
823  {
824  if (same)
825  {
827  u"Model '%1' same %2 '%3'");
828  return CStatusMessage(msgSame) << model.getModelStringAndDbKey() << description << newValue;
829  }
830  else
831  {
832  static const CStatusMessage msgDiff({ CLogCategories::validation() }, CStatusMessage::SeverityInfo,
833  u"Model '%1' changed %2 '%3'->'%4'");
834  return CStatusMessage(msgDiff) << model.getModelStringAndDbKey() << description << oldValue << newValue;
835  }
836  }
837 
839  {
840  if (!dbModel.isLoadedFromDb())
841  {
843  u"No DB model yet");
844  if (details) { details->push_back(msgNoDbModel); }
845  return false;
846  }
847 
848  CStatusMessageList validationMsgs;
849  bool changed = false;
850  bool equal = dbModel.getLivery().isLoadedFromDb() && dbModel.getLivery().isDbEqual(this->getLivery());
851  if (details)
852  {
853  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("livery"),
855  this->getLivery().getCombinedCodePlusInfoAndId()));
856  }
857  changed |= !equal;
858 
859  equal = dbModel.getAircraftIcaoCode().isLoadedFromDb() &&
861  if (details)
862  {
863  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("aircraft ICAO"),
865  this->getAircraftIcaoCode().getDesignatorDbKey()));
866  }
867  changed |= !equal;
868 
869  equal = dbModel.getDistributor().isLoadedFromDb() && dbModel.getDistributor().isDbEqual(this->getDistributor());
870  if (details)
871  {
872  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("distributor"),
873  dbModel.getDistributor().getDescription(),
874  this->getDistributor().getDescription()));
875  }
876  changed |= !equal;
877 
878  equal = dbModel.getSimulator() == this->getSimulator();
879  if (details)
880  {
881  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("simulator"),
882  dbModel.getSimulator().toQString(),
883  this->getSimulator().toQString()));
884  }
885  changed |= !equal;
886 
887  equal = dbModel.getDescription() == this->getDescription();
888  if (details)
889  {
890  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("description"), dbModel.getDescription(),
891  this->getDescription()));
892  }
893  changed |= !equal;
894 
895  equal = dbModel.getName() == this->getName();
896  if (details)
897  {
898  validationMsgs.push_back(
899  equalMessage(equal, *this, QStringLiteral("name"), dbModel.getName(), this->getName()));
900  }
901  changed |= !equal;
902 
903  equal = dbModel.getModelMode() == this->getModelMode();
904  if (details)
905  {
906  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("mode"), dbModel.getModelModeAsString(),
907  this->getModelModeAsString()));
908  }
909  changed |= !equal;
910 
911  equal = dbModel.getCG() == this->getCG();
912  if (details)
913  {
914  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("CG"), dbModel.getCG().toQString(true),
915  this->getCG().toQString(true)));
916  }
917  changed |= !equal;
918 
919  equal = dbModel.getSupportedParts() == this->getSupportedParts();
920  if (details)
921  {
922  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("Supported parts"),
923  dbModel.getSupportedParts(), this->getSupportedParts()));
924  }
925  changed |= !equal;
926 
927  // clean messages
928  if (changed && details)
929  {
930  // we have a changed entity, remove the warnings as they are just noise
932  }
933 
934  if (details) { details->push_back(validationMsgs); }
935  return !changed;
936  }
937 
939  {
940  static const QString queried("queried");
941  static const QString matching("matching");
942  static const QString db("database");
943  static const QString def("map.default");
944  static const QString ownSim("own simulator");
945  static const QString set("manually set");
946  static const QString fsinn("FSInn");
947  static const QString probe("probe");
948  static const QString reverse("reverse lookup");
949  static const QString unknown("unknown");
950 
951  switch (type)
952  {
953  case TypeQueriedFromNetwork: return queried;
954  case TypeModelMatching: return matching;
955  case TypeDatabaseEntry: return db;
956  case TypeManuallySet: return set;
957  case TypeFSInnData: return fsinn;
958  case TypeTerrainProbe: return probe;
959  case TypeReverseLookup: return reverse;
960  case TypeOwnSimulatorModel: return ownSim;
961  case TypeModelMatchingDefaultModel: return def;
962  case TypeUnknown:
963  default: return unknown;
964  }
965  }
966 
968  {
970  if (n.count('/') < 2) { return n; }
971  return n.section('/', -2, -1);
972  }
973 
975  {
976  if (mode.isEmpty() || mode.startsWith('I', Qt::CaseInsensitive)) { return Include; }
977  if (mode.startsWith('E', Qt::CaseInsensitive)) { return Exclude; }
978  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "wrong mode");
979  return Include; // default
980  }
981 
983  {
984  static const QString i("Include");
985  static const QString e("Exclude");
986 
987  switch (mode)
988  {
989  case Include: return i;
990  case Exclude: return e;
991  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode");
992  }
993  return i; // default
994  }
995 
996  CAircraftModel CAircraftModel::fromDatabaseJsonBaseImpl(const QJsonObject &json, const QString &prefix,
997  const CAircraftIcaoCode &aircraftIcao,
998  const CLivery &livery, const CDistributor &distributor)
999  {
1000  const QString modelString(json.value(prefix % u"modelstring").toString());
1001  const QString modelStringAlias(json.value(prefix % u"modelstringalias").toString());
1002  const QString modelDescription(json.value(prefix % u"description").toString());
1003  const QString modelName(json.value(prefix % u"name").toString());
1004  const QString modelMode(json.value(prefix % u"mode").toString());
1005  const QString parts(json.value(prefix % u"parts").toString());
1006 
1007  // check for undefined to rule out 0ft values
1008  const QJsonValue cgjv = json.value(prefix % u"cgft");
1009  const CLength cg =
1010  (cgjv.isNull() || cgjv.isUndefined()) ? CLength::null() : CLength(cgjv.toDouble(), CLengthUnit::ft());
1011 
1012  const CSimulatorInfo simInfo = CSimulatorInfo::fromDatabaseJson(json, prefix);
1013  CAircraftModel model(modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription);
1014  model.setModelStringAlias(modelStringAlias);
1015  model.setModelModeAsString(modelMode);
1016  model.setSupportedParts(parts);
1017  model.setCG(cg);
1018  model.setKeyVersionTimestampFromDatabaseJson(json, prefix);
1019  model.setDistributor(distributor);
1020  model.setAircraftIcaoCode(aircraftIcao);
1021  model.setLivery(livery);
1022  return model;
1023  }
1024 
1026  {
1027  static const QString prefixAircraftIcao("ac_");
1028  static const QString prefixLivery("liv_");
1029  static const QString prefixDistributor("dist_");
1030  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1031  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1032  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1033 
1034  CDistributor distributor(CDistributor::fromDatabaseJson(json, prefixDistributor));
1035  CAircraftIcaoCode aircraftIcao(CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1036  CLivery livery(CLivery::fromDatabaseJson(json, prefixLivery));
1037 
1038  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1039  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1040  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1041 
1042  // full model
1043  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1044  }
1045 
1047  const AircraftCategoryIdMap &categories,
1048  LiveryIdMap &liveries, DistributorIdMap &distributors,
1049  const QString &prefix)
1050  {
1051  static const QString prefixAircraftIcao("ac_");
1052  static const QString prefixLivery("liv_");
1053  static const QString prefixDistributor("dist_");
1054  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1055  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1056  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1057 
1058  const bool cachedAircraftIcao = (idAircraftIcao >= 0) && aircraftIcaos.contains(idAircraftIcao);
1059  const bool cachedLivery = (idLivery >= 0) && liveries.contains(idLivery);
1060  const bool cachedDistributor = !idDistributor.isEmpty() && distributors.contains(idDistributor);
1061 
1062  CAircraftIcaoCode aircraftIcao(cachedAircraftIcao ?
1063  aircraftIcaos[idAircraftIcao] :
1064  CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1065 
1066  CLivery livery(cachedLivery ? liveries[idLivery] : CLivery::fromDatabaseJson(json, prefixLivery));
1067 
1068  CDistributor distributor(cachedDistributor ? distributors[idDistributor] :
1069  CDistributor::fromDatabaseJson(json, prefixDistributor));
1070 
1071  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1072  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1073  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1074 
1075  // update category
1076  if (!cachedAircraftIcao)
1077  {
1078  const int catId = aircraftIcao.getCategory().getDbKey();
1079  if (catId >= 0 && categories.contains(catId)) { aircraftIcao.setCategory(categories[catId]); }
1080  }
1081 
1082  // store in temp.cache
1083  if (!cachedAircraftIcao && aircraftIcao.isLoadedFromDb())
1084  {
1085  aircraftIcaos[aircraftIcao.getDbKey()] = aircraftIcao;
1086  }
1087  if (!cachedLivery && livery.isLoadedFromDb()) { liveries[livery.getDbKey()] = livery; }
1088  if (!cachedDistributor && distributor.isLoadedFromDb()) { distributors[distributor.getDbKey()] = distributor; }
1089 
1090  // full model
1091  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1092  }
1093 
1095  {
1096  static const QString ag("swift auto generated");
1097  return ag;
1098  }
1099 
1101  {
1102  if (p.isEmpty()) { return {}; }
1103  QString pc = removeChars(p.toUpper(), [](QChar c) { return !supportedParts().contains(c); });
1104  std::sort(pc.begin(), pc.end());
1105  return pc;
1106  }
1107 
1109  {
1110  static const QString p("EFGLS");
1111  return p;
1112  }
1113 } // namespace swift::misc::simulation
static Qt::CaseSensitivity osFileNameCaseSensitivity()
Case sensitivity for current OS.
Definition: fileutils.cpp:224
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:95
static QString fixWindowsUncPath(const QString &filePath)
UNC file paths on Qt start with "/", but UNC file paths only work when they start with "//".
Definition: fileutils.cpp:438
static QString normalizeFilePathToQtStandard(const QString &filePath)
Normalize file path to Qt standard, e.g by turning \ to /.
Definition: fileutils.cpp:179
Value object for icons. An icon is stored in the global icon repository and identified by its index....
Definition: icon.h:39
static const CIcon & iconByIndex(CIcons::IconIndex index)
Icon for given index.
Definition: icon.cpp:54
static const QString & validation()
Validation.
Definition: logcategories.h:38
A sequence of log categories.
Memoizer for Ts. Other types are passed through.
Definition: memotable.h:49
decltype(auto) maybeMemoize(const T &member)
If T is in Ts, return the index of member in the memo table. Otherwise, return member.
Definition: memotable.h:53
Unmemoizer for Ts. Other types are passed through.
Definition: memotable.h:69
auto maybeUnmemoize(T &member) const
If T is in Ts, return proxy that will assign to member through the value at the given index in the fl...
Definition: memotable.h:81
Derived & validationError(const char16_t(&format)[N])
Set the severity to error, providing a format string, and adding the validation category.
Derived & validationWarning(const char16_t(&format)[N])
Set the severity to warning, providing a format string, and adding the validation category.
Derived & validationInfo(const char16_t(&format)[N])
Set the severity to info, providing a format string, and adding the validation category.
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.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
constexpr static auto SeverityError
Status severities.
constexpr static auto SeverityInfo
Status severities.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
void removeSeverity(CStatusMessage::StatusSeverity severity)
Remove given severity.
Mix of the most commonly used mixin classes.
Definition: valueobject.h:114
static bool canHandleIndex(CPropertyIndexRef index)
Can given index be handled.
Definition: orderable.cpp:31
int comparePropertyByIndex(CPropertyIndexRef index, const IOrderable &compareValue) const
Compare for index.
Definition: orderable.cpp:71
void setOrder(int order)
Set order.
Definition: orderable.h:35
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: orderable.cpp:55
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: orderable.cpp:38
int getOrder() const
Order.
Definition: orderable.h:29
void updateMissingParts(const ITimestampBased &other)
Update missing parts.
QString getFormattedUtcTimestampYmdhms() const
As yyyy MM dd HH mm ss.
void setUtcTimestamp(const QDateTime &timestamp)
Set timestamp.
Value object for ICAO classification.
bool hasDesignator() const
Aircraft designator?
bool hasKnownDesignator() const
Has designator and designator is not "ZZZZ".
bool isVtol() const
Is VTOL aircraft (helicopter, tilt wing)
QVariant propertyByIndex(swift::misc::CPropertyIndexRef index) const
Property by index.
QString getDesignatorDbKey() const
Designator and DB key.
void setPropertyByIndex(swift::misc::CPropertyIndexRef index, const QVariant &variant)
Set property by index.
int comparePropertyByIndex(CPropertyIndexRef index, const CAircraftIcaoCode &compareValue) const
Compare for index.
void updateMissingParts(const CAircraftIcaoCode &otherIcaoCode)
Update missing parts.
CStatusMessageList validate() const
Validate data.
int calculateScore(const CAircraftIcaoCode &otherCode, CStatusMessageList *log=nullptr) const
Considers rank, manufacturer and family 0..100.
void setDesignator(const QString &icaoDesignator)
Set ICAO designator, e.g. "B737".
const CAircraftCategory & getCategory() const
Get category.
void setCategory(const CAircraftCategory &category)
Set category.
Value object for ICAO classification.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
int comparePropertyByIndex(CPropertyIndexRef index, const CCallsign &compareValue) const
Compare for index.
Definition: callsign.cpp:341
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
bool isEmpty() const
Is empty?
Definition: callsign.h:63
void setTypeHint(TypeHint hint)
Type hint.
Definition: callsign.h:114
Value object encapsulating information about an airpot.
Definition: livery.h:29
bool isMilitary() const
Military livery.
Definition: livery.h:98
bool setAirlineIcaoCode(const CAirlineIcaoCode &airlineIcao)
Airline ICAO code.
Definition: livery.cpp:79
CStatusMessageList validate() const
Validate data.
Definition: livery.cpp:126
const CAirlineIcaoCode & getAirlineIcaoCode() const
Corresponding airline, if any.
Definition: livery.h:65
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: livery.cpp:334
QString getCombinedCodePlusInfoAndId() const
Combined code, info, plus id.
Definition: livery.cpp:69
bool hasValidAirlineDesignator() const
Airline available?
Definition: livery.cpp:155
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: livery.cpp:354
bool isColorLivery() const
Color livery?
Definition: livery.cpp:178
int comparePropertyByIndex(CPropertyIndexRef index, const CLivery &compareValue) const
Compare for index.
Definition: livery.cpp:379
int calculateScore(const CLivery &otherLivery, bool preferColorLiveries=false, CStatusMessageList *log=nullptr) const
Score by comparison to another livery 0..100.
Definition: livery.cpp:439
void updateMissingParts(const CLivery &otherLivery)
Update missing parts.
Definition: livery.cpp:404
QJsonValue getDbKeyAsJsonValue() const
Key as JSON value, or null.
Definition: datastore.cpp:73
QString getDbKeyAsString() const
DB key as string.
Definition: datastore.cpp:30
bool isLoadedFromDb() const
Loaded from DB.
Definition: datastore.cpp:49
void setDbKey(int key)
Set the DB key.
Definition: datastore.h:96
bool isDbEqual(const IDatastoreObjectWithIntegerKey &other) const
Same DB key and hence equal.
Definition: datastore.h:105
QString getDbKeyAsStringInParentheses(const QString &prefix={}) const
Db key in parentheses, e.g. "(3)".
Definition: datastore.cpp:36
void setKeyVersionTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix=QString())
Set key and timestamp values.
Definition: datastore.cpp:79
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
bool isLoadedFromDb() const
Loaded from DB.
Definition: datastore.h:204
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:195
bool isDbEqual(const IDatastoreObjectWithStringKey &other) const
Same DB key and hence equal.
Definition: datastore.h:198
const QString & getDbKey() const
Get DB key.
Definition: datastore.h:180
void setDbKey(const QString &key)
Set the DB key.
Definition: datastore.h:192
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: mixinindex.h:158
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: mixinindex.h:165
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Physical unit length (length)
Definition: length.h:18
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
int comparePropertyByIndex(CPropertyIndexRef index, const PQ &pq) const
Compare for index.
double value(MU unit) const
Value in given unit.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
void setFileTimestamp(const QDateTime &timestampUtc)
Set file timestamp.
QString getAllModelStringsAndAliases() const
Get model string and aliases.
CAircraftModel()=default
Default constructor.
QString getSwiftLiveryString(bool aircraftIcao=true, bool livery=true, bool model=true) const
swift livery string (to be sent via network)
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
void convertFromMemoizedJson(const QJsonObject &json, const MemoHelper::CUnmemoizer &)
From JSON with memoized members (used by CAircraftModelList)
bool hasValidFileTimestamp() const
Valid file timestamp?
void setName(const QString &name)
Name.
bool setDistributorOrder(int order)
Set the distributor order.
bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string?
bool hasValidSimulator() const
Valid simulator.
ModelMode getModelMode() const
Model mode.
bool hasValidAircraftAndAirlineDesignator() const
Valid airline and aircraft designator?
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code.
void updateByExistingDirectories(const CAircraftModel &otherModel)
Update the directories from other model.
bool hasKnownAircraftDesignator() const
Has known aircraft designator?
bool hasCategory() const
Assigned a category?
bool hasAirlineDesignator() const
Airline designator?
static CAircraftModel fromDatabaseJsonCaching(const QJsonObject &json, aviation::AircraftIcaoIdMap &aircraftIcaos, const aviation::AircraftCategoryIdMap &categories, aviation::LiveryIdMap &liveries, DistributorIdMap &distributors, const QString &prefix=QString("mod_"))
From swift DB JSON, caching during this process (faster)
bool hasExistingCorrespondingFile() const
Does the corresponding file exist?
bool hasManuallySetString() const
Model string which was manually set.
static const QString & supportedParts()
Supported parts.
const QString & getSupportedParts() const
Supported parts.
bool hasModelStringAlias() const
Non empty model string alias?
static DBTripleIds parseNetworkLiveryString(const QString &liveryString)
Split swift network string.
static const QString & liveryStringPrefix()
Livery string prefix.
void setModelStringAlias(const QString &alias)
Model string alias.
QString convertToQString(bool i18n=false) const
Cast as QString.
const aviation::CLivery & getLivery() const
Get livery.
QString getMembersDbStatus() const
Info, which members (Livery, Aircraft ICAO, ...) are already based on DB data.
bool isInPath(const QString &path, Qt::CaseSensitivity cs) const
Is the file in the give path.
QString getFileDirectoryPath() const
Directory path if any.
ModelMode
Mode, decides if a model is supposed to be used in the model set for model matching or not....
Definition: aircraftmodel.h:92
int calculateScore(const CAircraftModel &compareModel, bool preferColorLiveries, CStatusMessageList *log=nullptr) const
Calculate score.
void setModelString(const QString &modelString)
Model string.
void setModelModeAsString(const QString &mode)
Set model mode as string.
void setFileDetailsAndTimestamp(const QFileInfo &fileInfo)
Set file timestamp, timestamp and file name.
bool hasAircraftAndAirlineDesignator() const
Designators.
void updateLocalFileNames(const CAircraftModel &model)
Update file names from local model.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
CSimulatorInfo getSimulator() const
Simulator info.
QString getAllModelStringsAliasesAndDbKey() const
Get model string and aliases.
void normalizeFileNameForDb()
File path for DB (absolute paths make no sense in DB)
@ TypeModelMatchingDefaultModel
a default model assigned by model matching
Definition: aircraftmodel.h:81
@ TypeTerrainProbe
peudo aircraft used for terrain probing (FSX)
Definition: aircraftmodel.h:86
@ TypeFSInnData
model based on FSD ICAO data
Definition: aircraftmodel.h:78
@ TypeQueriedFromNetwork
model was queried by network protocol (ICAO data)
Definition: aircraftmodel.h:77
@ TypeOwnSimulatorModel
represents own simulator model (AI model, model on disk)
Definition: aircraftmodel.h:84
@ TypeDatabaseEntry
used along with mapping definition
Definition: aircraftmodel.h:82
@ TypeReverseLookup
reverse lookup model
Definition: aircraftmodel.h:79
@ TypeModelMatching
model is result of model matching
Definition: aircraftmodel.h:80
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
static const QString & modelTypeToString(ModelType type)
Model type.
void setCallsign(const aviation::CCallsign &callsign)
Corresponding callsign if applicable.
bool isEqualForPublishing(const CAircraftModel &dbModel, CStatusMessageList *details=nullptr) const
Considered equal for publishing, compares if livery etc. are the same DB values.
bool canInitializeFromFsd() const
Can be initialized from FSD.
void setFileName(const QString &fileName)
File name.
bool hasDistributor() const
Distributor, but not necessarily loaded from DB.
static QString cleanUpPartsString(const QString &p)
Clean up parts string.
CStatusMessageList verifyModelData() const
Verify the model data.
QString getFormattedFileTimestampYmdhms() const
File timestamp.
bool isCivilian() const
Civilian model?
void setSupportedParts(const QString &supportedParts)
Supported parts.
bool adjustLocalFileNames(const QString &newModelDir, const QString &stripModelDirIndicator={})
Adjust file names to a new directory.
static ModelMode modelModeFromString(const QString &mode)
Model mode.
const physical_quantities::CLength & getCG() const
Get center of gravity.
bool matchesDbDistributor(const CDistributor &distributor) const
By distributor.
const QString & getName() const
Name.
const QString & getModelModeAsString() const
Model mode as string.
bool hasAircraftDesignator() const
Has aircraft designator?
const QString & getDescription() const
Descriptive text.
QJsonObject toDatabaseJson() const
To database JSON.
void setDescription(const QString &description)
Descriptive text.
void setSimulator(const CSimulatorInfo &simulator)
Set simulator info.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
bool matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string or alias?
const CIcon & getModelModeAsIcon() const
Model mode as string.
void setCG(const physical_quantities::CLength &cg)
Get center of gravity.
CStatusMessageList validate(bool withNestedObjects) const
Validate.
bool matchesMode(simulation::CAircraftModel::ModelModeFilter mode) const
Matches given mode?
const CDistributor & getDistributor() const
Get distributor.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Aircraft ICAO code.
static const QString & modelModeToString(ModelMode mode)
Model mode.
bool matchesFileName(const QString &fileName) const
Matching file name?
ModelType getModelType() const
Model type.
bool hasQueriedModelString() const
Queried model string?
static CAircraftModel fromDatabaseJson(const QJsonObject &json, const QString &prefix=QString("mod_"))
From swift DB JSON.
bool hasDbDistributor() const
Distributor loaded from DB.
QString getModelStringAndDbKey() const
Model string and DB key (if available)
bool matchesSimulatorFlag(CSimulatorInfo::Simulator simulator) const
Matches given simulator?
int comparePropertyByIndex(CPropertyIndexRef index, const CAircraftModel &compareValue) const
Compare for index.
const QString & getModelTypeAsString() const
Model type.
const QString & getModelStringAlias() const
Model key, either queried or loaded from simulator model.
void setAircraftIcaoDesignator(const QString &designator)
Set aircraft ICAO code designator.
bool matchesAnyDbDistributor(const CDistributorList &distributors) const
By distributor.
QDateTime getFileTimestamp() const
Get timestamp.
QString asHtmlSummary(const QString &separator="<br>") const
As a brief HTML summary (e.g. used in tooltips)
void setModelMode(ModelMode mode)
Set model mode.
bool hasDescription(bool ignoreAutoGenerated=false) const
Description.
bool hasAnyModelString() const
Any model string or alias?
bool isMilitary() const
Military model?
QJsonObject toMemoizedJson(MemoHelper::CMemoizer &) const
To JSON with memoized members (used by CAircraftModelList)
static bool isSwiftLiveryString(const QString &liveryString)
swift livery string
bool hasModelString() const
Non empty model string?
void setLivery(const aviation::CLivery &livery)
Livery.
void setDistributor(const CDistributor &distributor)
Set distributor.
void setAircraftIcaoCodes(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CAirlineIcaoCode &airlineIcaoCode)
Set ICAO codes.
bool setAircraftIcaoCode(const aviation::CAircraftIcaoCode &aircraftIcaoCode)
Set aircraft ICAO code.
QString toDatabaseJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Compact) const
To database JSON.
static const QString & autoGenerated()
Hint, that model was automatically generated (e.g. by auto stashing)
const QString & getFileName() const
File name (corresponding data for simulator, only available if representing simulator model.
void updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority=true)
Update missing parts from another model.
bool matchesSimulator(const CSimulatorInfo &simulator) const
Matches given simulator?
Value object encapsulating information of software distributor.
Definition: distributor.h:33
int comparePropertyByIndex(CPropertyIndexRef index, const CDistributor &compareValue) const
Compare for index.
QVariant propertyByIndex(swift::misc::CPropertyIndexRef index) const
Property by index.
Definition: distributor.cpp:57
const QString & getDescription() const
Get description.
Definition: distributor.h:56
void setPropertyByIndex(swift::misc::CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: distributor.cpp:77
void updateMissingParts(const CDistributor &otherDistributor)
Update missing parts.
static CDistributor fromDatabaseJson(const QJsonObject &json, const QString &prefix=QString())
Object from JSON.
swift::misc::CStatusMessageList validate() const
Validate data.
Value object encapsulating a list of distributors.
CDistributor findByKeyOrAlias(const QString &keyOrAlias) const
Find by id or alias.
bool matchesAnyKeyOrAlias(const QString &keyOrAlias) const
At least is matching key or alias.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
int comparePropertyByIndex(CPropertyIndexRef index, const CSimulatorInfo &compareValue) const
Cast as QString.
Simulator getSimulator() const
Simulator.
static CSimulatorInfo fromDatabaseJson(const QJsonObject &json, const QString &prefix)
From database JSON.
bool isAnySimulator() const
Any simulator?
SWIFT_MISC_EXPORT QString inApostrophes(const QString &in, bool ignoreEmpty=false)
Return string in apostrophes.
QString removeChars(const QString &s, F predicate)
Return a string with characters removed that match the given predicate.
Definition: stringutils.h:34
SWIFT_MISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs)
String compare.
QDateTime fromMSecsSinceEpoch(qint64 msecs)
bool isValid() const const
qint64 toMSecsSinceEpoch() const const
QString toString(QStringView format) const const
QString absolutePath() const const
QDir absoluteDir() const const
QString absoluteFilePath() const const
bool exists(const QString &path)
bool isReadable() const const
QDateTime lastModified() const const
QByteArray toJson(QJsonDocument::JsonFormat format) const const
QJsonValueRef value() const const
QJsonObject::iterator end()
QJsonObject::iterator find(QLatin1StringView key)
QJsonObject::iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
bool isNull() const const
bool isUndefined() const const
double toDouble(double defaultValue) const const
int toInt(int defaultValue) const const
QString toString() const const
bool contains(const Key &key) const const
qsizetype count() const const
QString arg(Args &&... args) const const
QString::iterator begin()
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString::iterator end()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) &&
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, QString::SectionFlags flags) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QString toLower() const const
QString toUpper() const const
CaseInsensitive
QTimeZone utc()
bool canConvert() const const
QVariant fromValue(T &&value)
QString toString() const const
int typeId() const const
T value() const &const
Simple literal type containing a single QLatin1String.
Definition: metaclass.h:100
Type wrapper for passing MetaFlag to CMetaMember::has.
Definition: metaclass.h:117
int livery
livery id, by that I have airline id
Definition: aircraftmodel.h:52
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
Definition: valueobject.h:67
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26