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  introspect<CAircraftModel>().forEachMember([&, this](auto member) {
136  if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
137  {
138  auto &&maybeMemo = helper.maybeMemoize(member.in(*this));
139  json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(maybeMemo));
140  }
141  });
142  return json;
143  }
144 
146  {
147  introspect<CAircraftModel>().forEachMember([&, this](auto member) {
148  if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
149  {
150  auto it = json.find(CExplicitLatin1String(member.latin1Name()));
151  if (it != json.end()) { it.value() >> helper.maybeUnmemoize(member.in(*this)).get(); }
152  }
153  });
154  }
155 
157  {
158  return QStringLiteral(
159  "Model: %1 changed: %2%3Simulator: %4 Mode: %5 Distributor: %6%7Aircraft ICAO: %8%9Livery: %10")
160  .arg(this->getModelStringAndDbKey(), this->getFormattedUtcTimestampYmdhms(), separator,
161  this->getSimulator().toQString(true), this->getModelModeAsString(),
162  this->getDistributor().getIdAndDescription(), separator, this->getAircraftIcaoCode().asHtmlSummary(),
163  separator)
164  .arg(this->getLivery().asHtmlSummary("&nbsp;"))
165  .replace(" ", "&nbsp;");
166  }
167 
168  bool CAircraftModel::matchesFileName(const QString &fileName) const
169  {
170  return stringCompare(fileName, m_fileName, CFileUtils::osFileNameCaseSensitivity());
171  }
172 
174  {
175  CStatusMessageList msgs;
176  const ModelType t = this->getModelType();
179  {
180  if (!this->hasExistingCorrespondingFile())
181  {
182  const CStatusMessage m = CStatusMessage(this).validationWarning(u"File '%1' not readable")
183  << this->getFileName();
184  msgs.push_back(m);
185  }
186  else
187  {
188  const CStatusMessage m = CStatusMessage(this).validationInfo(u"File '%1' existing")
189  << this->getFileName();
190  msgs.push_back(m);
191  }
192  }
193  else
194  {
195  const CStatusMessage m = CStatusMessage(this).validationError(u"Invalid model type to check: '%1'")
196  << this->getModelTypeAsString();
197  msgs.push_back(m);
198  }
199  return msgs;
200  }
201 
203  {
204  const bool nw = this->getModelType() == CAircraftModel::TypeQueriedFromNetwork ||
206  this->getModelType() == CAircraftModel::TypeUnknown;
207  return nw;
208  }
209 
211  {
212  m_callsign = callsign;
213  m_callsign.setTypeHint(CCallsign::Aircraft);
214  }
215 
217  {
218  if (this->hasValidDbKey())
219  {
220  return this->hasModelString() ? this->getModelString() % this->getDbKeyAsStringInParentheses(" ") :
221  this->getDbKeyAsString();
222  }
223  else { return this->getModelString(); }
224  }
225 
227  {
228  if (!this->hasModelStringAlias()) { return m_modelString; }
229  if (!this->hasModelString()) { return m_modelStringAlias; }
230  return m_modelString % u", " % m_modelStringAlias;
231  }
232 
234  {
235  if (!this->hasModelStringAlias() || m_modelString.isEmpty()) { return this->getModelStringAndDbKey(); }
236  if (!this->isLoadedFromDb()) { return this->getAllModelStringsAndAliases(); }
237  return this->getAllModelStringsAndAliases() % " " % this->getDbKeyAsStringInParentheses();
238  }
239 
240  bool CAircraftModel::isVtol() const { return this->getAircraftIcaoCode().isVtol(); }
241 
243  {
244  if (index.isMyself()) { return QVariant::fromValue(*this); }
245  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
246  {
247  return IDatastoreObjectWithIntegerKey::propertyByIndex(index);
248  }
249  if (IOrderable::canHandleIndex(index)) { return IOrderable::propertyByIndex(index); }
250 
251  const ColumnIndex i = index.frontCasted<ColumnIndex>();
252  switch (i)
253  {
254  case IndexModelString: return QVariant(m_modelString);
255  case IndexModelStringAlias: return QVariant(m_modelStringAlias);
257  case IndexHasQueriedModelString: return QVariant::fromValue(this->hasQueriedModelString());
258  case IndexModelType: return QVariant::fromValue(m_modelType);
259  case IndexModelTypeAsString: return QVariant(this->getModelTypeAsString());
260  case IndexModelMode: return QVariant::fromValue(m_modelMode);
261  case IndexModelModeAsString: return QVariant::fromValue(this->getModelModeAsString());
262  case IndexModelModeAsIcon: return QVariant::fromValue(this->getModelModeAsIcon());
263  case IndexDistributor: return m_distributor.propertyByIndex(index.copyFrontRemoved());
264  case IndexSimulatorInfo: return m_simulator.propertyByIndex(index.copyFrontRemoved());
265  case IndexSimulatorInfoAsString: return QVariant(m_simulator.toQString());
266  case IndexDescription: return QVariant(m_description);
267  case IndexName: return QVariant(m_name);
268  case IndexFileName: return QVariant(m_fileName);
269  case IndexCG: return m_cg.propertyByIndex(index.copyFrontRemoved());
270  case IndexSupportedParts: return QVariant(m_supportedParts);
271  case IndexFileTimestamp: return QVariant::fromValue(this->getFileTimestamp());
272  case IndexFileTimestampFormattedYmdhms: return QVariant::fromValue(this->getFormattedFileTimestampYmdhms());
273  case IndexAircraftIcaoCode: return m_aircraftIcao.propertyByIndex(index.copyFrontRemoved());
274  case IndexLivery: return m_livery.propertyByIndex(index.copyFrontRemoved());
275  case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved());
276  case IndexMembersDbStatus: return this->getMembersDbStatus();
277  default: return CValueObject::propertyByIndex(index);
278  }
279  }
280 
282  {
283  if (index.isMyself())
284  {
285  (*this) = variant.value<CAircraftModel>();
286  return;
287  }
288  if (IOrderable::canHandleIndex(index))
289  {
290  IOrderable::setPropertyByIndex(index, variant);
291  return;
292  }
293  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
294  {
295  IDatastoreObjectWithIntegerKey::setPropertyByIndex(index, variant);
296  return;
297  }
298 
299  const ColumnIndex i = index.frontCasted<ColumnIndex>();
300  switch (i)
301  {
302  case IndexModelString: m_modelString = variant.toString(); break;
303  case IndexModelStringAlias: m_modelStringAlias = variant.toString(); break;
304  case IndexAircraftIcaoCode: m_aircraftIcao.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
305  case IndexLivery: m_livery.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
306  case IndexDistributor: m_distributor.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
307  case IndexDescription: m_description = variant.toString(); break;
308  case IndexSimulatorInfo: m_simulator.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
309  case IndexName: m_name = variant.toString(); break;
310  case IndexCG: m_cg.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
311  case IndexSupportedParts: this->setSupportedParts(variant.toString()); break;
312  case IndexModelType: m_modelType = variant.value<ModelType>(); break;
313  case IndexFileName: m_fileName = variant.toString(); break;
314  case IndexCallsign:
315  m_callsign.setPropertyByIndex(index.copyFrontRemoved(), variant);
316  m_callsign.setTypeHint(CCallsign::Aircraft);
317  break;
318  case IndexFileTimestamp:
319  if (variant.canConvert<QDateTime>()) { this->setFileTimestamp(variant.value<QDateTime>()); }
320  else if (variant.canConvert<qint64>()) { m_fileTimestamp = variant.value<qint64>(); }
321  break;
322  case IndexModelMode:
323  if (static_cast<QMetaType::Type>(variant.typeId()) == QMetaType::QString)
324  {
325  this->setModelModeAsString(variant.toString());
326  }
327  else { m_modelMode = variant.value<ModelMode>(); }
328  break;
329  // no setter indexes ignored
330  case IndexHasQueriedModelString: break;
331  case IndexModelTypeAsString: break;
332  case IndexModelModeAsString: break;
333  case IndexModelModeAsIcon: break;
334  case IndexFileTimestampFormattedYmdhms: break;
335  case IndexSimulatorInfoAsString: break;
336  case IndexMembersDbStatus: break;
337 
338  default: CValueObject::setPropertyByIndex(index, variant); break;
339  }
340  }
341 
343  {
344  if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
345  {
346  return IDatastoreObjectWithIntegerKey::comparePropertyByIndex(index, compareValue);
347  }
348  if (IOrderable::canHandleIndex(index)) { return IOrderable::comparePropertyByIndex(index, compareValue); }
349  if (index.isMyself()) { return m_modelString.compare(compareValue.getModelString(), Qt::CaseInsensitive); }
350  const ColumnIndex i = index.frontCasted<ColumnIndex>();
351  switch (i)
352  {
353  case IndexModelString: return m_modelString.compare(compareValue.getModelString(), Qt::CaseInsensitive);
354  case IndexModelStringAlias:
355  return m_modelStringAlias.compare(compareValue.getModelStringAlias(), Qt::CaseInsensitive);
359  case IndexHasQueriedModelString:
360  return Compare::compare(this->hasQueriedModelString(), compareValue.hasQueriedModelString());
361  case IndexAircraftIcaoCode:
362  return m_aircraftIcao.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getAircraftIcaoCode());
363  case IndexLivery: return m_livery.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getLivery());
364  case IndexDistributor:
365  return m_distributor.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getDistributor());
366  case IndexDescription: return m_description.compare(compareValue.getDescription(), Qt::CaseInsensitive);
367  case IndexName: return m_name.compare(compareValue.getName(), Qt::CaseInsensitive);
368  case IndexCallsign:
369  return m_callsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCallsign());
370  case IndexFileName: return m_fileName.compare(compareValue.getFileName(), Qt::CaseInsensitive);
371  case IndexCG: return m_cg.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCG());
372  case IndexSupportedParts: return m_supportedParts.compare(compareValue.getSupportedParts());
373  case IndexModelTypeAsString:
374  case IndexModelType: return Compare::compare(m_modelType, compareValue.getModelType());
375  case IndexSimulatorInfoAsString:
376  case IndexSimulatorInfo:
377  return m_simulator.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getSimulator());
378  case IndexFileTimestamp:
379  case IndexFileTimestampFormattedYmdhms: return Compare::compare(m_fileTimestamp, compareValue.m_fileTimestamp);
380  case IndexModelMode:
381  case IndexModelModeAsString:
382  case IndexModelModeAsIcon: return Compare::compare(m_modelMode, compareValue.getModelMode());
383  case IndexMembersDbStatus: return getMembersDbStatus().compare(compareValue.getMembersDbStatus());
384  default: break;
385  }
386  Q_ASSERT_X(false, Q_FUNC_INFO, "No comparison");
387  return 0;
388  }
389 
391  {
392  if (m_aircraftIcao == aircraftIcaoCode) { return false; }
393  m_aircraftIcao = aircraftIcaoCode;
394  return true;
395  }
396 
398  {
399  m_aircraftIcao.setDesignator(designator);
400  }
401 
403  const CAirlineIcaoCode &airlineIcaoCode)
404  {
405  m_aircraftIcao = aircraftIcaoCode;
406  m_livery.setAirlineIcaoCode(airlineIcaoCode);
407  }
408 
410  {
411  return this->hasKnownAircraftDesignator() && m_livery.hasValidAirlineDesignator();
412  }
413 
414  bool CAircraftModel::hasAircraftDesignator() const { return m_aircraftIcao.hasDesignator(); }
415 
416  bool CAircraftModel::hasKnownAircraftDesignator() const { return m_aircraftIcao.hasKnownDesignator(); }
417 
418  bool CAircraftModel::hasCategory() const { return m_aircraftIcao.hasCategory(); }
419 
421 
423  {
424  return this->hasAircraftDesignator() && this->hasAirlineDesignator();
425  }
426 
428  {
429  return this->getAircraftIcaoCode().isMilitary() || this->getLivery().isMilitary();
430  }
431 
432  bool CAircraftModel::isCivilian() const { return !this->isMilitary(); }
433 
435  {
436  if (order < 0) { return false; }
437  if (!m_distributor.isLoadedFromDb()) { return false; }
438  m_distributor.setOrder(order);
439  return true;
440  }
441 
443  {
444  if (distributors.isEmpty()) { return false; }
445  bool found = false;
446  const int noDistributorOrder = distributors.size();
447  if (this->hasDbDistributor())
448  {
449  const CDistributor d = distributors.findByKeyOrAlias(m_distributor.getDbKey());
450  if (d.hasValidDbKey())
451  {
452  m_distributor.setOrder(d.getOrder());
453  found = true;
454  }
455  else { m_distributor.setOrder(noDistributorOrder); }
456  }
457  else { m_distributor.setOrder(noDistributorOrder); }
458  return found;
459  }
460 
461  bool CAircraftModel::hasDbDistributor() const { return m_distributor.isLoadedFromDb(); }
462 
464  {
465  return m_distributor.hasValidDbKey(); // key is valid, but not guaranteed from DB
466  }
467 
468  bool CAircraftModel::matchesDbDistributor(const CDistributor &distributor) const
469  {
470  if (!distributor.isLoadedFromDb()) { return false; }
471  if (!this->hasDbDistributor()) { return false; }
472  return m_distributor.getDbKey() == distributor.getDbKey();
473  }
474 
476  {
477  if (distributors.isEmpty()) { return false; }
478  if (!this->hasDbDistributor()) { return false; }
479  return distributors.matchesAnyKeyOrAlias(m_distributor.getDbKey());
480  }
481 
482  void CAircraftModel::setSupportedParts(const QString &supportedParts)
483  {
485  }
486 
487  bool CAircraftModel::matchesMode(ModelModeFilter mode) const
488  {
489  if (mode == All) { return true; }
490  return (mode & m_modelMode) > 0;
491  }
492 
494  {
495  switch (this->getModelMode())
496  {
497  case Include: return CIcon::iconByIndex(CIcons::ModelInclude);
498  case Exclude: return CIcon::iconByIndex(CIcons::ModelExclude);
499  case Undefined: return CIcon::iconByIndex(CIcons::StandardIconUnknown16);
500  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode"); break;
501  }
502  return CIcon::iconByIndex(CIcons::ModelInclude);
503  }
504 
506  {
508  }
509 
511  {
512  return (static_cast<int>(simulator.getSimulator()) & static_cast<int>(this->getSimulator().getSimulator())) > 0;
513  }
514 
515  bool CAircraftModel::matchesSimulatorFlag(CSimulatorInfo::Simulator simulator) const
516  {
517  return (static_cast<int>(simulator) & static_cast<int>(this->getSimulator().getSimulator())) > 0;
518  }
519 
521  {
522  return this->hasValidFileTimestamp() ? QDateTime::fromMSecsSinceEpoch(m_fileTimestamp, QTimeZone::utc()) :
523  QDateTime();
524  }
525 
527  {
528  return this->hasValidFileTimestamp() ? this->getFileTimestamp().toString("yyyy-MM-dd HH:mm:ss") : "";
529  }
530 
531  bool CAircraftModel::hasValidFileTimestamp() const { return m_fileTimestamp >= 0; }
532 
533  void CAircraftModel::setFileTimestamp(const QDateTime &timestampUtc)
534  {
535  m_fileTimestamp = timestampUtc.isValid() ? timestampUtc.toMSecsSinceEpoch() : -1;
536  }
537 
538  void CAircraftModel::setFileTimestamp(qint64 timestamp) { m_fileTimestamp = (timestamp < 0) ? -1 : timestamp; }
539 
541  {
542  this->setFileName(fileInfo.absoluteFilePath());
543  const QDateTime modified = fileInfo.lastModified();
544  if (modified.isValid())
545  {
546  this->setFileTimestamp(modified);
547  this->setUtcTimestamp(modified);
548  }
549  else
550  {
551  const QDateTime created = fileInfo.lastModified();
552  this->setFileTimestamp(created);
553  this->setUtcTimestamp(created);
554  }
555  }
556 
558  {
559  static const QString p("swift_");
560  return p;
561  }
562 
564  {
565  return (liveryString.length() > liveryStringPrefix().length() &&
567  }
568 
569  QString CAircraftModel::getSwiftLiveryString(bool aircraftIcao, bool livery, bool model) const
570  {
571  if (!aircraftIcao && !livery && !model) { return QString(); }
572  const QString l =
573  (livery && this->getLivery().hasValidDbKey() ? u'l' % this->getLivery().getDbKeyAsString() : QString()) %
574  (aircraftIcao && this->getAircraftIcaoCode().hasValidDbKey() ?
575  QStringLiteral("a") % this->getAircraftIcaoCode().getDbKeyAsString() :
576  QString()) %
577  (model && this->hasValidDbKey() ? u'm' % this->getDbKeyAsString() : QString());
578 
579  return l.isEmpty() ? QString() : liveryStringPrefix() % l;
580  }
581 
583  {
584  return (sim.isFG()) ? this->getSwiftLiveryString(true, false, false) : this->getSwiftLiveryString();
585  }
586 
588  {
589  // "swift_m22l33a11"
590  if (!CAircraftModel::isSwiftLiveryString(liveryString)) { return DBTripleIds(); }
591 
592  DBTripleIds ids;
593  const QString ls = liveryString.mid(liveryStringPrefix().length()).toLower();
594  for (int c = 0; c < ls.length(); c++)
595  {
596  const QChar m = ls[c];
597  if ((m == 'm' || m == 'a' || m == 'l') && (c + 1) < ls.length())
598  {
599  const int cs = c + 1;
600  int cc = cs;
601  while (cc < ls.length() && ls[cc].isDigit()) { cc++; } // find end of id
602  if (cc > cs)
603  {
604  const QString idString = ls.mid(cs, cc - cs);
605  const int id = idString.toInt();
606  c = cc - 1; // +1 again in for
607 
608  if (m == 'm') { ids.model = id; }
609  else if (m == 'a') { ids.aircraft = id; }
610  else if (m == 'l') { ids.livery = id; }
611  }
612  }
613  }
614  return ids;
615  }
616 
617  void CAircraftModel::updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority)
618  {
619  if (dbModelPriority && !this->hasValidDbKey() && otherModel.hasValidDbKey())
620  {
621  // we have no DB data, but the other one has
622  // so we change roles. We take the DB object as base, and update our parts
623  CAircraftModel copy(otherModel);
624  copy.updateMissingParts(*this);
625  *this = copy;
626  return;
627  }
628 
629  // local file names and file timestamp
630  this->updateLocalFileNames(otherModel);
631  if (m_fileTimestamp < 0 || otherModel.m_fileTimestamp > m_fileTimestamp)
632  {
633  this->setFileTimestamp(otherModel.getFileTimestamp());
634  }
635 
636  // both are DB data, treat as being the same except for filename maybe
637  if (this->hasValidDbKey() && otherModel.hasValidDbKey()) { return; }
638 
639  // update attributes where applicable
640  if (m_callsign.isEmpty()) { this->setCallsign(otherModel.getCallsign()); }
641  if (m_modelString.isEmpty()) { this->setModelString(otherModel.getModelString()); }
642  if (m_name.isEmpty()) { this->setName(otherModel.getName()); }
643  if (m_modelType == TypeUnknown) { m_modelType = otherModel.getModelType(); }
644  if (m_modelMode == Undefined) { m_modelType = otherModel.getModelType(); }
645  if (m_description.isEmpty() || m_description.startsWith(CAircraftModel::autoGenerated(), Qt::CaseInsensitive))
646  {
647  this->setDescription(otherModel.getDescription());
648  }
649  if (this->getSimulator().isUnspecified())
650  {
651  // simulator can only be overridden as simulators can also be removed
652  this->setSimulator(otherModel.getSimulator());
653  }
654 
656  m_livery.updateMissingParts(otherModel.getLivery());
657  m_aircraftIcao.updateMissingParts(otherModel.getAircraftIcaoCode());
658  m_distributor.updateMissingParts(otherModel.getDistributor());
659  }
660 
662  {
663  if (otherModel.hasExistingCorrespondingFile()) { this->setFileName(otherModel.getFileName()); }
664  }
665 
667  {
668  return m_modelType == TypeQueriedFromNetwork && this->hasModelString();
669  }
670 
672  {
673  return m_modelType == TypeManuallySet && this->hasModelString();
674  }
675 
676  bool CAircraftModel::hasDescription(bool ignoreAutoGenerated) const
677  {
678  if (m_description.isEmpty()) { return false; }
679  if (!ignoreAutoGenerated) { return true; }
680  return (!this->getDescription().startsWith(autoGenerated(), Qt::CaseInsensitive));
681  }
682 
683  bool CAircraftModel::hasValidSimulator() const { return m_simulator.isAnySimulator(); }
684 
686  {
687  return (this->isLoadedFromDb() ? QStringLiteral("M") : QStringLiteral("m")) %
688  (this->getDistributor().isLoadedFromDb() ? QStringLiteral("D") : QStringLiteral("d")) %
689  (this->getAircraftIcaoCode().isLoadedFromDb() ? QStringLiteral("A") : QStringLiteral("a")) %
690  (this->getLivery().isLoadedFromDb() && getLivery().isColorLivery() ?
691  QStringLiteral("C-") :
692  (this->getLivery().isLoadedFromDb() ? QStringLiteral("L") : QStringLiteral("l")) %
693  (this->getLivery().getAirlineIcaoCode().isLoadedFromDb() ? QStringLiteral("A") :
694  QStringLiteral("a")));
695  }
696 
698 
700  {
702  {
703  // this is already a local model, ignore
704  return;
705  }
706 
708  {
709  // other local, priority
710  this->setFileName(model.getFileName());
711  return;
712  }
713 
714  // both not local, override empty values
715  if (m_fileName.isEmpty()) { this->setFileName(model.getFileName()); }
716  }
717 
718  bool CAircraftModel::adjustLocalFileNames(const QString &newModelDir, const QString &stripModelDirIndicator)
719  {
720  if (!this->hasFileName()) { return false; }
721  const QString md = CFileUtils::normalizeFilePathToQtStandard(newModelDir);
722  int i = -1;
723  if (stripModelDirIndicator.isEmpty())
724  {
725  QString strip = md.mid(md.lastIndexOf('/'));
726  i = m_fileName.lastIndexOf(strip);
727  }
728  else { i = m_fileName.lastIndexOf(stripModelDirIndicator); }
729  if (i < 0) { return false; }
730  m_fileName = CFileUtils::appendFilePaths(newModelDir, m_fileName.mid(i));
731  return true;
732  }
733 
735  {
736  if (!this->hasFileName()) { return false; }
738  if (!fi.exists()) { return false; }
739  const bool r = fi.isReadable();
740  return r;
741  }
742 
744  {
745  if (!this->hasFileName()) { return QDir(); }
747  return fi.absoluteDir();
748  }
749 
751  {
752  if (!this->hasFileName()) { return {}; }
753  return this->getFileDirectory().absolutePath();
754  }
755 
757  {
758  const QString p(this->getFileDirectoryPath());
759  if (path.isEmpty() || p.isEmpty()) { return false; }
760  if (path.startsWith('/'))
761  {
762  if (path.endsWith('/')) { return p.contains(path.mid(1, path.length() - 2), cs); }
763  return p.contains(path.mid(1));
764  }
765  if (path.endsWith('/')) { return p.contains(path.left(path.length() - 1), cs); }
766  return (p.contains(path, cs));
767  }
768 
769  bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
770  {
771  return stringCompare(modelString, m_modelString, sensitivity);
772  }
773 
774  bool CAircraftModel::matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
775  {
776  if (this->matchesModelString(modelString, sensitivity)) { return true; }
777  return stringCompare(modelString, m_modelStringAlias, sensitivity);
778  }
779 
780  int CAircraftModel::calculateScore(const CAircraftModel &compareModel, bool preferColorLiveries,
781  CStatusMessageList *log) const
782  {
783  const int icaoScore = this->getAircraftIcaoCode().calculateScore(compareModel.getAircraftIcaoCode(), log);
784  const int liveryScore = this->getLivery().calculateScore(compareModel.getLivery(), preferColorLiveries, log);
785  CCallsign::addLogDetailsToList(
786  log, this->getCallsign(),
787  QStringLiteral("ICAO score: %1 | livery score: %2").arg(icaoScore).arg(liveryScore));
788  return qRound(0.5 * (icaoScore + liveryScore));
789  }
790 
791  CStatusMessageList CAircraftModel::validate(bool withNestedObjects) const
792  {
793  static const CLogCategoryList cats(CLogCategoryList(this).withValidation());
794  CStatusMessageList msgs;
795  if (!hasModelString())
796  {
797  msgs.push_back(
798  CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: missing model string (aka key)"));
799  }
800  if (!hasValidSimulator())
801  {
802  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: no simulator set"));
803  }
804  // as of T34 made description optional, lines can be removed after 6/2017
805  // if (!hasDescription()) {msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, u"Model: no
806  // description")); }
807  if (withNestedObjects)
808  {
809  msgs.push_back(m_aircraftIcao.validate());
810  msgs.push_back(m_livery.validate());
811  msgs.push_back(m_distributor.validate());
812  }
813  return msgs;
814  }
815 
817  CStatusMessage equalMessage(bool same, const CAircraftModel &model, const QString &description,
818  const QString &oldValue, const QString &newValue)
819  {
820  if (same)
821  {
823  u"Model '%1' same %2 '%3'");
824  return CStatusMessage(msgSame) << model.getModelStringAndDbKey() << description << newValue;
825  }
826  else
827  {
828  static const CStatusMessage msgDiff({ CLogCategories::validation() }, CStatusMessage::SeverityInfo,
829  u"Model '%1' changed %2 '%3'->'%4'");
830  return CStatusMessage(msgDiff) << model.getModelStringAndDbKey() << description << oldValue << newValue;
831  }
832  }
833 
835  {
836  if (!dbModel.isLoadedFromDb())
837  {
839  u"No DB model yet");
840  if (details) { details->push_back(msgNoDbModel); }
841  return false;
842  }
843 
844  CStatusMessageList validationMsgs;
845  bool changed = false;
846  bool equal = dbModel.getLivery().isLoadedFromDb() && dbModel.getLivery().isDbEqual(this->getLivery());
847  if (details)
848  {
849  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("livery"),
851  this->getLivery().getCombinedCodePlusInfoAndId()));
852  }
853  changed |= !equal;
854 
855  equal = dbModel.getAircraftIcaoCode().isLoadedFromDb() &&
857  if (details)
858  {
859  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("aircraft ICAO"),
861  this->getAircraftIcaoCode().getDesignatorDbKey()));
862  }
863  changed |= !equal;
864 
865  equal = dbModel.getDistributor().isLoadedFromDb() && dbModel.getDistributor().isDbEqual(this->getDistributor());
866  if (details)
867  {
868  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("distributor"),
869  dbModel.getDistributor().getDescription(),
870  this->getDistributor().getDescription()));
871  }
872  changed |= !equal;
873 
874  equal = dbModel.getSimulator() == this->getSimulator();
875  if (details)
876  {
877  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("simulator"),
878  dbModel.getSimulator().toQString(),
879  this->getSimulator().toQString()));
880  }
881  changed |= !equal;
882 
883  equal = dbModel.getDescription() == this->getDescription();
884  if (details)
885  {
886  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("description"), dbModel.getDescription(),
887  this->getDescription()));
888  }
889  changed |= !equal;
890 
891  equal = dbModel.getName() == this->getName();
892  if (details)
893  {
894  validationMsgs.push_back(
895  equalMessage(equal, *this, QStringLiteral("name"), dbModel.getName(), this->getName()));
896  }
897  changed |= !equal;
898 
899  equal = dbModel.getModelMode() == this->getModelMode();
900  if (details)
901  {
902  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("mode"), dbModel.getModelModeAsString(),
903  this->getModelModeAsString()));
904  }
905  changed |= !equal;
906 
907  equal = dbModel.getCG() == this->getCG();
908  if (details)
909  {
910  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("CG"), dbModel.getCG().toQString(true),
911  this->getCG().toQString(true)));
912  }
913  changed |= !equal;
914 
915  equal = dbModel.getSupportedParts() == this->getSupportedParts();
916  if (details)
917  {
918  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("Supported parts"),
919  dbModel.getSupportedParts(), this->getSupportedParts()));
920  }
921  changed |= !equal;
922 
923  // clean messages
924  if (changed && details)
925  {
926  // we have a changed entity, remove the warnings as they are just noise
928  }
929 
930  if (details) { details->push_back(validationMsgs); }
931  return !changed;
932  }
933 
935  {
936  static const QString queried("queried");
937  static const QString matching("matching");
938  static const QString db("database");
939  static const QString def("map.default");
940  static const QString ownSim("own simulator");
941  static const QString set("manually set");
942  static const QString fsinn("FSInn");
943  static const QString probe("probe");
944  static const QString reverse("reverse lookup");
945  static const QString unknown("unknown");
946 
947  switch (type)
948  {
949  case TypeQueriedFromNetwork: return queried;
950  case TypeModelMatching: return matching;
951  case TypeDatabaseEntry: return db;
952  case TypeManuallySet: return set;
953  case TypeFSInnData: return fsinn;
954  case TypeTerrainProbe: return probe;
955  case TypeReverseLookup: return reverse;
956  case TypeOwnSimulatorModel: return ownSim;
957  case TypeModelMatchingDefaultModel: return def;
958  case TypeUnknown:
959  default: return unknown;
960  }
961  }
962 
964  {
966  if (n.count('/') < 2) { return n; }
967  return n.section('/', -2, -1);
968  }
969 
971  {
972  if (mode.isEmpty() || mode.startsWith('I', Qt::CaseInsensitive)) { return Include; }
973  if (mode.startsWith('E', Qt::CaseInsensitive)) { return Exclude; }
974  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "wrong mode");
975  return Include; // default
976  }
977 
979  {
980  static const QString i("Include");
981  static const QString e("Exclude");
982 
983  switch (mode)
984  {
985  case Include: return i;
986  case Exclude: return e;
987  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode");
988  }
989  return i; // default
990  }
991 
992  CAircraftModel CAircraftModel::fromDatabaseJsonBaseImpl(const QJsonObject &json, const QString &prefix,
993  const CAircraftIcaoCode &aircraftIcao,
994  const CLivery &livery, const CDistributor &distributor)
995  {
996  const QString modelString(json.value(prefix % u"modelstring").toString());
997  const QString modelStringAlias(json.value(prefix % u"modelstringalias").toString());
998  const QString modelDescription(json.value(prefix % u"description").toString());
999  const QString modelName(json.value(prefix % u"name").toString());
1000  const QString modelMode(json.value(prefix % u"mode").toString());
1001  const QString parts(json.value(prefix % u"parts").toString());
1002 
1003  // check for undefined to rule out 0ft values
1004  const QJsonValue cgjv = json.value(prefix % u"cgft");
1005  const CLength cg =
1006  (cgjv.isNull() || cgjv.isUndefined()) ? CLength::null() : CLength(cgjv.toDouble(), CLengthUnit::ft());
1007 
1008  const CSimulatorInfo simInfo = CSimulatorInfo::fromDatabaseJson(json, prefix);
1009  CAircraftModel model(modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription);
1010  model.setModelStringAlias(modelStringAlias);
1011  model.setModelModeAsString(modelMode);
1012  model.setSupportedParts(parts);
1013  model.setCG(cg);
1014  model.setKeyVersionTimestampFromDatabaseJson(json, prefix);
1015  model.setDistributor(distributor);
1016  model.setAircraftIcaoCode(aircraftIcao);
1017  model.setLivery(livery);
1018  return model;
1019  }
1020 
1022  {
1023  static const QString prefixAircraftIcao("ac_");
1024  static const QString prefixLivery("liv_");
1025  static const QString prefixDistributor("dist_");
1026  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1027  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1028  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1029 
1030  CDistributor distributor(CDistributor::fromDatabaseJson(json, prefixDistributor));
1031  CAircraftIcaoCode aircraftIcao(CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1032  CLivery livery(CLivery::fromDatabaseJson(json, prefixLivery));
1033 
1034  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1035  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1036  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1037 
1038  // full model
1039  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1040  }
1041 
1043  const AircraftCategoryIdMap &categories,
1044  LiveryIdMap &liveries, DistributorIdMap &distributors,
1045  const QString &prefix)
1046  {
1047  static const QString prefixAircraftIcao("ac_");
1048  static const QString prefixLivery("liv_");
1049  static const QString prefixDistributor("dist_");
1050  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1051  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1052  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1053 
1054  const bool cachedAircraftIcao = (idAircraftIcao >= 0) && aircraftIcaos.contains(idAircraftIcao);
1055  const bool cachedLivery = (idLivery >= 0) && liveries.contains(idLivery);
1056  const bool cachedDistributor = !idDistributor.isEmpty() && distributors.contains(idDistributor);
1057 
1058  CAircraftIcaoCode aircraftIcao(cachedAircraftIcao ?
1059  aircraftIcaos[idAircraftIcao] :
1060  CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1061 
1062  CLivery livery(cachedLivery ? liveries[idLivery] : CLivery::fromDatabaseJson(json, prefixLivery));
1063 
1064  CDistributor distributor(cachedDistributor ? distributors[idDistributor] :
1065  CDistributor::fromDatabaseJson(json, prefixDistributor));
1066 
1067  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1068  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1069  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1070 
1071  // update category
1072  if (!cachedAircraftIcao)
1073  {
1074  const int catId = aircraftIcao.getCategory().getDbKey();
1075  if (catId >= 0 && categories.contains(catId)) { aircraftIcao.setCategory(categories[catId]); }
1076  }
1077 
1078  // store in temp.cache
1079  if (!cachedAircraftIcao && aircraftIcao.isLoadedFromDb())
1080  {
1081  aircraftIcaos[aircraftIcao.getDbKey()] = aircraftIcao;
1082  }
1083  if (!cachedLivery && livery.isLoadedFromDb()) { liveries[livery.getDbKey()] = livery; }
1084  if (!cachedDistributor && distributor.isLoadedFromDb()) { distributors[distributor.getDbKey()] = distributor; }
1085 
1086  // full model
1087  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1088  }
1089 
1091  {
1092  static const QString ag("swift auto generated");
1093  return ag;
1094  }
1095 
1097  {
1098  if (p.isEmpty()) { return QString(); }
1099  QString pc = removeChars(p.toUpper(), [](QChar c) { return !supportedParts().contains(c); });
1100  std::sort(pc.begin(), pc.end());
1101  return pc;
1102  }
1103 
1105  {
1106  static const QString p("EFGLS");
1107  return p;
1108  }
1109 } // namespace swift::misc::simulation
static Qt::CaseSensitivity osFileNameCaseSensitivity()
Case sensitivity for current OS.
Definition: fileutils.cpp:227
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:444
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:33
int comparePropertyByIndex(CPropertyIndexRef index, const IOrderable &compareValue) const
Compare for index.
Definition: orderable.cpp:73
void setOrder(int order)
Set order.
Definition: orderable.h:35
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: orderable.cpp:57
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: orderable.cpp:40
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:81
CStatusMessageList validate() const
Validate data.
Definition: livery.cpp:128
const CAirlineIcaoCode & getAirlineIcaoCode() const
Corresponding airline, if any.
Definition: livery.h:65
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: livery.cpp:336
QString getCombinedCodePlusInfoAndId() const
Combined code, info, plus id.
Definition: livery.cpp:71
bool hasValidAirlineDesignator() const
Airline available?
Definition: livery.cpp:157
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: livery.cpp:356
bool isColorLivery() const
Color livery?
Definition: livery.cpp:180
int comparePropertyByIndex(CPropertyIndexRef index, const CLivery &compareValue) const
Compare for index.
Definition: livery.cpp:381
int calculateScore(const CLivery &otherLivery, bool preferColorLiveries=false, CStatusMessageList *log=nullptr) const
Score by comparison to another livery 0..100.
Definition: livery.cpp:441
void updateMissingParts(const CLivery &otherLivery)
Update missing parts.
Definition: livery.cpp:406
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.
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:59
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:79
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