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 
50  CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, const QString &description,
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 
69  QString CAircraftModel::convertToQString(bool i18n) const
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 
81  QJsonObject CAircraftModel::toDatabaseJson() const
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 
127  QString CAircraftModel::toDatabaseJsonString(QJsonDocument::JsonFormat format) const
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 
145  void CAircraftModel::convertFromMemoizedJson(const QJsonObject &json, const MemoHelper::CUnmemoizer &helper)
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 
156  QString CAircraftModel::asHtmlSummary(const QString &separator) const
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 
281  void CAircraftModel::setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
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.type()) == 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);
357  return this->getAllModelStringsAndAliases().compare(compareValue.getAllModelStringsAndAliases(),
358  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 
397  void CAircraftModel::setAircraftIcaoDesignator(const QString &designator)
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 
505  void CAircraftModel::setModelModeAsString(const QString &mode)
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, Qt::UTC) : QDateTime();
523  }
524 
526  {
527  return this->hasValidFileTimestamp() ? this->getFileTimestamp().toString("yyyy-MM-dd HH:mm:ss") : "";
528  }
529 
530  bool CAircraftModel::hasValidFileTimestamp() const { return m_fileTimestamp >= 0; }
531 
532  void CAircraftModel::setFileTimestamp(const QDateTime &timestampUtc)
533  {
534  m_fileTimestamp = timestampUtc.isValid() ? timestampUtc.toMSecsSinceEpoch() : -1;
535  }
536 
537  void CAircraftModel::setFileTimestamp(qint64 timestamp) { m_fileTimestamp = (timestamp < 0) ? -1 : timestamp; }
538 
539  void CAircraftModel::setFileDetailsAndTimestamp(const QFileInfo &fileInfo)
540  {
541  this->setFileName(fileInfo.absoluteFilePath());
542  const QDateTime modified = fileInfo.lastModified();
543  if (modified.isValid())
544  {
545  this->setFileTimestamp(modified);
546  this->setUtcTimestamp(modified);
547  }
548  else
549  {
550  const QDateTime created = fileInfo.lastModified();
551  this->setFileTimestamp(created);
552  this->setUtcTimestamp(created);
553  }
554  }
555 
557  {
558  static const QString p("swift_");
559  return p;
560  }
561 
562  bool CAircraftModel::isSwiftLiveryString(const QString &liveryString)
563  {
564  return (liveryString.length() > liveryStringPrefix().length() &&
565  liveryString.startsWith(liveryStringPrefix(), Qt::CaseInsensitive));
566  }
567 
568  QString CAircraftModel::getSwiftLiveryString(bool aircraftIcao, bool livery, bool model) const
569  {
570  if (!aircraftIcao && !livery && !model) { return QString(); }
571  const QString l =
572  (livery && this->getLivery().hasValidDbKey() ? u'l' % this->getLivery().getDbKeyAsString() : QString()) %
573  (aircraftIcao && this->getAircraftIcaoCode().hasValidDbKey() ?
574  QStringLiteral("a") % this->getAircraftIcaoCode().getDbKeyAsString() :
575  QString()) %
576  (model && this->hasValidDbKey() ? u'm' % this->getDbKeyAsString() : QString());
577 
578  return l.isEmpty() ? QString() : liveryStringPrefix() % l;
579  }
580 
582  {
583  return (sim.isFG()) ? this->getSwiftLiveryString(true, false, false) : this->getSwiftLiveryString();
584  }
585 
587  {
588  // "swift_m22l33a11"
589  if (!CAircraftModel::isSwiftLiveryString(liveryString)) { return DBTripleIds(); }
590 
591  DBTripleIds ids;
592  const QString ls = liveryString.mid(liveryStringPrefix().length()).toLower();
593  for (int c = 0; c < ls.length(); c++)
594  {
595  const QChar m = ls[c];
596  if ((m == 'm' || m == 'a' || m == 'l') && (c + 1) < ls.length())
597  {
598  const int cs = c + 1;
599  int cc = cs;
600  while (cc < ls.length() && ls[cc].isDigit()) { cc++; } // find end of id
601  if (cc > cs)
602  {
603  const QString idString = ls.mid(cs, cc - cs);
604  const int id = idString.toInt();
605  c = cc - 1; // +1 again in for
606 
607  if (m == 'm') { ids.model = id; }
608  else if (m == 'a') { ids.aircraft = id; }
609  else if (m == 'l') { ids.livery = id; }
610  }
611  }
612  }
613  return ids;
614  }
615 
616  void CAircraftModel::updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority)
617  {
618  if (dbModelPriority && !this->hasValidDbKey() && otherModel.hasValidDbKey())
619  {
620  // we have no DB data, but the other one has
621  // so we change roles. We take the DB object as base, and update our parts
622  CAircraftModel copy(otherModel);
623  copy.updateMissingParts(*this);
624  *this = copy;
625  return;
626  }
627 
628  // local file names and file timestamp
629  this->updateLocalFileNames(otherModel);
630  if (m_fileTimestamp < 0 || otherModel.m_fileTimestamp > m_fileTimestamp)
631  {
632  this->setFileTimestamp(otherModel.getFileTimestamp());
633  }
634 
635  // both are DB data, treat as being the same except for filename maybe
636  if (this->hasValidDbKey() && otherModel.hasValidDbKey()) { return; }
637 
638  // update attributes where applicable
639  if (m_callsign.isEmpty()) { this->setCallsign(otherModel.getCallsign()); }
640  if (m_modelString.isEmpty()) { this->setModelString(otherModel.getModelString()); }
641  if (m_name.isEmpty()) { this->setName(otherModel.getName()); }
642  if (m_modelType == TypeUnknown) { m_modelType = otherModel.getModelType(); }
643  if (m_modelMode == Undefined) { m_modelType = otherModel.getModelType(); }
644  if (m_description.isEmpty() || m_description.startsWith(CAircraftModel::autoGenerated(), Qt::CaseInsensitive))
645  {
646  this->setDescription(otherModel.getDescription());
647  }
648  if (this->getSimulator().isUnspecified())
649  {
650  // simulator can only be overridden as simulators can also be removed
651  this->setSimulator(otherModel.getSimulator());
652  }
653 
655  m_livery.updateMissingParts(otherModel.getLivery());
656  m_aircraftIcao.updateMissingParts(otherModel.getAircraftIcaoCode());
657  m_distributor.updateMissingParts(otherModel.getDistributor());
658  }
659 
661  {
662  if (otherModel.hasExistingCorrespondingFile()) { this->setFileName(otherModel.getFileName()); }
663  }
664 
666  {
667  return m_modelType == TypeQueriedFromNetwork && this->hasModelString();
668  }
669 
671  {
672  return m_modelType == TypeManuallySet && this->hasModelString();
673  }
674 
675  bool CAircraftModel::hasDescription(bool ignoreAutoGenerated) const
676  {
677  if (m_description.isEmpty()) { return false; }
678  if (!ignoreAutoGenerated) { return true; }
679  return (!this->getDescription().startsWith(autoGenerated(), Qt::CaseInsensitive));
680  }
681 
682  bool CAircraftModel::hasValidSimulator() const { return m_simulator.isAnySimulator(); }
683 
685  {
686  return (this->isLoadedFromDb() ? QStringLiteral("M") : QStringLiteral("m")) %
687  (this->getDistributor().isLoadedFromDb() ? QStringLiteral("D") : QStringLiteral("d")) %
688  (this->getAircraftIcaoCode().isLoadedFromDb() ? QStringLiteral("A") : QStringLiteral("a")) %
689  (this->getLivery().isLoadedFromDb() && getLivery().isColorLivery() ?
690  QStringLiteral("C-") :
691  (this->getLivery().isLoadedFromDb() ? QStringLiteral("L") : QStringLiteral("l")) %
692  (this->getLivery().getAirlineIcaoCode().isLoadedFromDb() ? QStringLiteral("A") :
693  QStringLiteral("a")));
694  }
695 
697 
699  {
701  {
702  // this is already a local model, ignore
703  return;
704  }
705 
707  {
708  // other local, priority
709  this->setFileName(model.getFileName());
710  return;
711  }
712 
713  // both not local, override empty values
714  if (m_fileName.isEmpty()) { this->setFileName(model.getFileName()); }
715  }
716 
717  bool CAircraftModel::adjustLocalFileNames(const QString &newModelDir, const QString &stripModelDirIndicator)
718  {
719  if (!this->hasFileName()) { return false; }
720  const QString md = CFileUtils::normalizeFilePathToQtStandard(newModelDir);
721  int i = -1;
722  if (stripModelDirIndicator.isEmpty())
723  {
724  QString strip = md.mid(md.lastIndexOf('/'));
725  i = m_fileName.lastIndexOf(strip);
726  }
727  else { i = m_fileName.lastIndexOf(stripModelDirIndicator); }
728  if (i < 0) { return false; }
729  m_fileName = CFileUtils::appendFilePaths(newModelDir, m_fileName.mid(i));
730  return true;
731  }
732 
734  {
735  if (!this->hasFileName()) { return false; }
736  const QFileInfo fi(CFileUtils::fixWindowsUncPath(this->getFileName()));
737  if (!fi.exists()) { return false; }
738  const bool r = fi.isReadable();
739  return r;
740  }
741 
743  {
744  if (!this->hasFileName()) { return QDir(); }
745  const QFileInfo fi(CFileUtils::fixWindowsUncPath(this->getFileName()));
746  return fi.absoluteDir();
747  }
748 
750  {
751  if (!this->hasFileName()) { return {}; }
752  return this->getFileDirectory().absolutePath();
753  }
754 
755  bool CAircraftModel::isInPath(const QString &path, Qt::CaseSensitivity cs) const
756  {
757  const QString p(this->getFileDirectoryPath());
758  if (path.isEmpty() || p.isEmpty()) { return false; }
759  if (path.startsWith('/'))
760  {
761  if (path.endsWith('/')) { return p.contains(path.mid(1, path.length() - 2), cs); }
762  return p.contains(path.mid(1));
763  }
764  if (path.endsWith('/')) { return p.contains(path.left(path.length() - 1), cs); }
765  return (p.contains(path, cs));
766  }
767 
768  bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
769  {
770  return stringCompare(modelString, m_modelString, sensitivity);
771  }
772 
773  bool CAircraftModel::matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
774  {
775  if (this->matchesModelString(modelString, sensitivity)) { return true; }
776  return stringCompare(modelString, m_modelStringAlias, sensitivity);
777  }
778 
779  int CAircraftModel::calculateScore(const CAircraftModel &compareModel, bool preferColorLiveries,
780  CStatusMessageList *log) const
781  {
782  const int icaoScore = this->getAircraftIcaoCode().calculateScore(compareModel.getAircraftIcaoCode(), log);
783  const int liveryScore = this->getLivery().calculateScore(compareModel.getLivery(), preferColorLiveries, log);
784  CCallsign::addLogDetailsToList(
785  log, this->getCallsign(),
786  QStringLiteral("ICAO score: %1 | livery score: %2").arg(icaoScore).arg(liveryScore));
787  return qRound(0.5 * (icaoScore + liveryScore));
788  }
789 
790  CStatusMessageList CAircraftModel::validate(bool withNestedObjects) const
791  {
792  static const CLogCategoryList cats(CLogCategoryList(this).withValidation());
793  CStatusMessageList msgs;
794  if (!hasModelString())
795  {
796  msgs.push_back(
797  CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: missing model string (aka key)"));
798  }
799  if (!hasValidSimulator())
800  {
801  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"Model: no simulator set"));
802  }
803  // as of T34 made description optional, lines can be removed after 6/2017
804  // if (!hasDescription()) {msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityWarning, u"Model: no
805  // description")); }
806  if (withNestedObjects)
807  {
808  msgs.push_back(m_aircraftIcao.validate());
809  msgs.push_back(m_livery.validate());
810  msgs.push_back(m_distributor.validate());
811  }
812  return msgs;
813  }
814 
816  CStatusMessage equalMessage(bool same, const CAircraftModel &model, const QString &description,
817  const QString &oldValue, const QString &newValue)
818  {
819  if (same)
820  {
822  u"Model '%1' same %2 '%3'");
823  return CStatusMessage(msgSame) << model.getModelStringAndDbKey() << description << newValue;
824  }
825  else
826  {
827  static const CStatusMessage msgDiff({ CLogCategories::validation() }, CStatusMessage::SeverityInfo,
828  u"Model '%1' changed %2 '%3'->'%4'");
829  return CStatusMessage(msgDiff) << model.getModelStringAndDbKey() << description << oldValue << newValue;
830  }
831  }
832 
834  {
835  if (!dbModel.isLoadedFromDb())
836  {
838  u"No DB model yet");
839  if (details) { details->push_back(msgNoDbModel); }
840  return false;
841  }
842 
843  CStatusMessageList validationMsgs;
844  bool changed = false;
845  bool equal = dbModel.getLivery().isLoadedFromDb() && dbModel.getLivery().isDbEqual(this->getLivery());
846  if (details)
847  {
848  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("livery"),
850  this->getLivery().getCombinedCodePlusInfoAndId()));
851  }
852  changed |= !equal;
853 
854  equal = dbModel.getAircraftIcaoCode().isLoadedFromDb() &&
856  if (details)
857  {
858  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("aircraft ICAO"),
860  this->getAircraftIcaoCode().getDesignatorDbKey()));
861  }
862  changed |= !equal;
863 
864  equal = dbModel.getDistributor().isLoadedFromDb() && dbModel.getDistributor().isDbEqual(this->getDistributor());
865  if (details)
866  {
867  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("distributor"),
868  dbModel.getDistributor().getDescription(),
869  this->getDistributor().getDescription()));
870  }
871  changed |= !equal;
872 
873  equal = dbModel.getSimulator() == this->getSimulator();
874  if (details)
875  {
876  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("simulator"),
877  dbModel.getSimulator().toQString(),
878  this->getSimulator().toQString()));
879  }
880  changed |= !equal;
881 
882  equal = dbModel.getDescription() == this->getDescription();
883  if (details)
884  {
885  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("description"), dbModel.getDescription(),
886  this->getDescription()));
887  }
888  changed |= !equal;
889 
890  equal = dbModel.getName() == this->getName();
891  if (details)
892  {
893  validationMsgs.push_back(
894  equalMessage(equal, *this, QStringLiteral("name"), dbModel.getName(), this->getName()));
895  }
896  changed |= !equal;
897 
898  equal = dbModel.getModelMode() == this->getModelMode();
899  if (details)
900  {
901  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("mode"), dbModel.getModelModeAsString(),
902  this->getModelModeAsString()));
903  }
904  changed |= !equal;
905 
906  equal = dbModel.getCG() == this->getCG();
907  if (details)
908  {
909  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("CG"), dbModel.getCG().toQString(true),
910  this->getCG().toQString(true)));
911  }
912  changed |= !equal;
913 
914  equal = dbModel.getSupportedParts() == this->getSupportedParts();
915  if (details)
916  {
917  validationMsgs.push_back(equalMessage(equal, *this, QStringLiteral("Supported parts"),
918  dbModel.getSupportedParts(), this->getSupportedParts()));
919  }
920  changed |= !equal;
921 
922  // clean messages
923  if (changed && details)
924  {
925  // we have a changed entity, remove the warnings as they are just noise
927  }
928 
929  if (details) { details->push_back(validationMsgs); }
930  return !changed;
931  }
932 
934  {
935  static const QString queried("queried");
936  static const QString matching("matching");
937  static const QString db("database");
938  static const QString def("map.default");
939  static const QString ownSim("own simulator");
940  static const QString set("manually set");
941  static const QString fsinn("FSInn");
942  static const QString probe("probe");
943  static const QString reverse("reverse lookup");
944  static const QString unknown("unknown");
945 
946  switch (type)
947  {
948  case TypeQueriedFromNetwork: return queried;
949  case TypeModelMatching: return matching;
950  case TypeDatabaseEntry: return db;
951  case TypeManuallySet: return set;
952  case TypeFSInnData: return fsinn;
953  case TypeTerrainProbe: return probe;
954  case TypeReverseLookup: return reverse;
955  case TypeOwnSimulatorModel: return ownSim;
956  case TypeModelMatchingDefaultModel: return def;
957  case TypeUnknown:
958  default: return unknown;
959  }
960  }
961 
962  QString CAircraftModel::normalizeFileNameForDb(const QString &filePath)
963  {
964  const QString n = CFileUtils::normalizeFilePathToQtStandard(filePath).toUpper();
965  if (n.count('/') < 2) { return n; }
966  return n.section('/', -2, -1);
967  }
968 
970  {
971  if (mode.isEmpty() || mode.startsWith('I', Qt::CaseInsensitive)) { return Include; }
972  if (mode.startsWith('E', Qt::CaseInsensitive)) { return Exclude; }
973  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "wrong mode");
974  return Include; // default
975  }
976 
978  {
979  static const QString i("Include");
980  static const QString e("Exclude");
981 
982  switch (mode)
983  {
984  case Include: return i;
985  case Exclude: return e;
986  default: Q_ASSERT_X(false, Q_FUNC_INFO, "wrong mode");
987  }
988  return i; // default
989  }
990 
991  CAircraftModel CAircraftModel::fromDatabaseJsonBaseImpl(const QJsonObject &json, const QString &prefix,
992  const CAircraftIcaoCode &aircraftIcao,
993  const CLivery &livery, const CDistributor &distributor)
994  {
995  const QString modelString(json.value(prefix % u"modelstring").toString());
996  const QString modelStringAlias(json.value(prefix % u"modelstringalias").toString());
997  const QString modelDescription(json.value(prefix % u"description").toString());
998  const QString modelName(json.value(prefix % u"name").toString());
999  const QString modelMode(json.value(prefix % u"mode").toString());
1000  const QString parts(json.value(prefix % u"parts").toString());
1001 
1002  // check for undefined to rule out 0ft values
1003  const QJsonValue cgjv = json.value(prefix % u"cgft");
1004  const CLength cg =
1005  (cgjv.isNull() || cgjv.isUndefined()) ? CLength::null() : CLength(cgjv.toDouble(), CLengthUnit::ft());
1006 
1007  const CSimulatorInfo simInfo = CSimulatorInfo::fromDatabaseJson(json, prefix);
1008  CAircraftModel model(modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription);
1009  model.setModelStringAlias(modelStringAlias);
1010  model.setModelModeAsString(modelMode);
1011  model.setSupportedParts(parts);
1012  model.setCG(cg);
1013  model.setKeyVersionTimestampFromDatabaseJson(json, prefix);
1014  model.setDistributor(distributor);
1015  model.setAircraftIcaoCode(aircraftIcao);
1016  model.setLivery(livery);
1017  return model;
1018  }
1019 
1020  CAircraftModel CAircraftModel::fromDatabaseJson(const QJsonObject &json, const QString &prefix)
1021  {
1022  static const QString prefixAircraftIcao("ac_");
1023  static const QString prefixLivery("liv_");
1024  static const QString prefixDistributor("dist_");
1025  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1026  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1027  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1028 
1029  CDistributor distributor(CDistributor::fromDatabaseJson(json, prefixDistributor));
1030  CAircraftIcaoCode aircraftIcao(CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1031  CLivery livery(CLivery::fromDatabaseJson(json, prefixLivery));
1032 
1033  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1034  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1035  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1036 
1037  // full model
1038  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1039  }
1040 
1042  const AircraftCategoryIdMap &categories,
1043  LiveryIdMap &liveries, DistributorIdMap &distributors,
1044  const QString &prefix)
1045  {
1046  static const QString prefixAircraftIcao("ac_");
1047  static const QString prefixLivery("liv_");
1048  static const QString prefixDistributor("dist_");
1049  const QString idDistributor = json.value(prefixDistributor % u"id").toString();
1050  const int idAircraftIcao = json.value(prefixAircraftIcao % u"id").toInt(-1);
1051  const int idLivery = json.value(prefixLivery % u"id").toInt(-1);
1052 
1053  const bool cachedAircraftIcao = (idAircraftIcao >= 0) && aircraftIcaos.contains(idAircraftIcao);
1054  const bool cachedLivery = (idLivery >= 0) && liveries.contains(idLivery);
1055  const bool cachedDistributor = !idDistributor.isEmpty() && distributors.contains(idDistributor);
1056 
1057  CAircraftIcaoCode aircraftIcao(cachedAircraftIcao ?
1058  aircraftIcaos[idAircraftIcao] :
1059  CAircraftIcaoCode::fromDatabaseJson(json, prefixAircraftIcao));
1060 
1061  CLivery livery(cachedLivery ? liveries[idLivery] : CLivery::fromDatabaseJson(json, prefixLivery));
1062 
1063  CDistributor distributor(cachedDistributor ? distributors[idDistributor] :
1064  CDistributor::fromDatabaseJson(json, prefixDistributor));
1065 
1066  if (!aircraftIcao.isLoadedFromDb() && idAircraftIcao >= 0) { aircraftIcao.setDbKey(idAircraftIcao); }
1067  if (!livery.isLoadedFromDb() && idLivery >= 0) { livery.setDbKey(idLivery); }
1068  if (!distributor.isLoadedFromDb() && !idDistributor.isEmpty()) { distributor.setDbKey(idDistributor); }
1069 
1070  // update category
1071  if (!cachedAircraftIcao)
1072  {
1073  const int catId = aircraftIcao.getCategory().getDbKey();
1074  if (catId >= 0 && categories.contains(catId)) { aircraftIcao.setCategory(categories[catId]); }
1075  }
1076 
1077  // store in temp.cache
1078  if (!cachedAircraftIcao && aircraftIcao.isLoadedFromDb())
1079  {
1080  aircraftIcaos[aircraftIcao.getDbKey()] = aircraftIcao;
1081  }
1082  if (!cachedLivery && livery.isLoadedFromDb()) { liveries[livery.getDbKey()] = livery; }
1083  if (!cachedDistributor && distributor.isLoadedFromDb()) { distributors[distributor.getDbKey()] = distributor; }
1084 
1085  // full model
1086  return CAircraftModel::fromDatabaseJsonBaseImpl(json, prefix, aircraftIcao, livery, distributor);
1087  }
1088 
1090  {
1091  static const QString ag("swift auto generated");
1092  return ag;
1093  }
1094 
1095  QString CAircraftModel::cleanUpPartsString(const QString &p)
1096  {
1097  if (p.isEmpty()) { return QString(); }
1098  QString pc = removeChars(p.toUpper(), [](QChar c) { return !supportedParts().contains(c); });
1099  std::sort(pc.begin(), pc.end());
1100  return pc;
1101  }
1102 
1104  {
1105  static const QString p("EFGLS");
1106  return p;
1107  }
1108 } // 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:160
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: mixinindex.h:167
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
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:35
SWIFT_MISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs)
String compare.
Simple literal type containing a single QLatin1String.
Definition: metaclass.h:102
Type wrapper for passing MetaFlag to CMetaMember::has.
Definition: metaclass.h:119
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