swift
aircraftmodellist.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 <tuple>
7 
8 #include <QDir>
9 #include <QFileInfo>
10 #include <QJsonValue>
11 #include <QList>
12 #include <QMultiMap>
13 #include <QStringBuilder>
14 
15 #include "config/buildconfig.h"
16 #include "misc/aviation/callsign.h"
17 #include "misc/directoryutils.h"
18 #include "misc/fileutils.h"
19 #include "misc/iterator.h"
20 #include "misc/math/mathutils.h"
23 #include "misc/range.h"
24 #include "misc/setbuilder.h"
25 #include "misc/statusmessage.h"
26 #include "misc/stringutils.h"
27 #include "misc/swiftdirectories.h"
28 
29 using namespace swift::config;
30 using namespace swift::misc::network;
31 using namespace swift::misc::math;
32 using namespace swift::misc::aviation;
33 using namespace swift::misc::physical_quantities;
34 
35 SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::simulation, CAircraftModel, CAircraftModelList)
36 
37 namespace swift::misc::simulation
38 {
39  CAircraftModelList::CAircraftModelList() {}
40 
41  CAircraftModelList::CAircraftModelList(const CSequence<CAircraftModel> &other) : CSequence<CAircraftModel>(other) {}
42 
43  bool CAircraftModelList::containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
44  {
45  for (const CAircraftModel &model : (*this))
46  {
47  if (model.matchesModelString(modelString, sensitivity)) { return true; }
48  }
49  return false;
50  }
51 
53  Qt::CaseSensitivity sensitivity) const
54  {
55  for (const CAircraftModel &m : (*this))
56  {
57  if (m.hasValidDbKey() && m.getDbKey() == model.getDbKey()) { return true; }
58  if (m.matchesModelString(model.getModelString(), sensitivity)) { return true; }
59  }
60  return false;
61  }
62 
63  bool CAircraftModelList::containsCallsign(const CCallsign &callsign) const
64  {
65  return this->contains(&CAircraftModel::getCallsign, callsign);
66  }
67 
68  bool CAircraftModelList::containsCombinedType(const QString &combinedType) const
69  {
70  if (combinedType.isEmpty()) { return false; }
71  const QString ct(combinedType.toUpper().trimmed());
72  return this->containsBy(
73  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getCombinedType() == ct; });
74  }
75 
76  bool CAircraftModelList::containsModelsWithAircraftIcaoDesignator(const QString &aircraftDesignator) const
77  {
78  return this->contains(&CAircraftModel::getAircraftIcaoCodeDesignator, aircraftDesignator);
79  }
80 
82  const QString &airlineDesignator) const
83  {
84  return this->contains(&CAircraftModel::getAircraftIcaoCodeDesignator, aircraftDesignator,
86  }
87 
89  {
90  if (!airline.hasValidDesignator()) { return false; }
91  return this->contains(&CAircraftModel::getAirlineIcaoCode, airline);
92  }
93 
95  Qt::CaseSensitivity sensitivity) const
96  {
97  return this->findBy(
98  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
99  }
100 
102  {
103  return this->findBy([&](const CAircraftModel &model) { return !model.hasModelString(); });
104  }
105 
107  Qt::CaseSensitivity sensitivity) const
108  {
109  if (modelString.isEmpty()) { return CAircraftModel(); }
110  return this->findFirstByOrDefault(
111  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
112  }
113 
115  Qt::CaseSensitivity sensitivity) const
116  {
117  if (modelString.isEmpty()) { return CAircraftModel(); }
118  return this->findFirstByOrDefault(
119  [&](const CAircraftModel &model) { return model.matchesModelStringOrAlias(modelString, sensitivity); });
120  }
121 
123  {
124  if (callsign.isEmpty()) { return CAircraftModel(); }
125  return this->findFirstByOrDefault([&](const CAircraftModel &model) { return model.getCallsign() == callsign; });
126  }
127 
129  const CAirlineIcaoCode &airlineIcaoCode) const
130  {
131  const QString aircraft(aircraftIcaoCode.getDesignator());
132  const QString airline(airlineIcaoCode.getDesignator());
133 
134  if (airline.isEmpty())
135  {
136  return this->findBy(
137  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getDesignator() == aircraft; });
138  }
139  if (aircraft.isEmpty())
140  {
141  return this->findBy(
142  [&](const CAircraftModel &model) { return model.getAirlineIcaoCode().getDesignator() == airline; });
143  }
144  return this->findBy([&](const CAircraftModel &model) {
145  return model.getAirlineIcaoCode().getDesignator() == airline &&
146  model.getAircraftIcaoCode().getDesignator() == aircraft;
147  });
148  }
149 
151  const CAirlineIcaoCode &airlineIcaoCode) const
152  {
154  airlineIcaoCode);
155  }
156 
159  const QString &combinedCode) const
160  {
161  if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); }
162  return this->findBy([&](const CAircraftModel &model) {
163  if (!model.getAircraftIcaoCode().matchesDesignator(aircraftDesignator)) { return false; }
164  return model.getLivery().matchesCombinedCode(combinedCode);
165  });
166  }
167 
169  const CLivery &livery) const
170  {
171  return this->findBy(&CAircraftModel::getAircraftIcaoCode, aircraftIcaoCode, &CAircraftModel::getLivery, livery);
172  }
173 
175  {
176  const int id = airline.getGroupId();
177  if (id < 0) return {};
178  return this->findBy([&](const CAircraftModel &model) { return model.getAirlineIcaoCode().getGroupId() == id; });
179  }
180 
182  const QString &telephony,
183  bool onlyIfExistInModel) const
184  {
185  return this->findBy([&](const CAircraftModel &model) {
186  if (!model.hasAirlineDesignator() || !model.hasValidDbKey()) { return false; }
187  const CAirlineIcaoCode &icao = model.getAirlineIcaoCode();
188  if (!name.isEmpty() && (icao.hasName() || !onlyIfExistInModel))
189  {
190  if (!icao.getName().contains(name, Qt::CaseInsensitive)) { return false; }
191  }
192 
193  if (!telephony.isEmpty() && (icao.hasTelephonyDesignator() || !onlyIfExistInModel))
194  {
195  if (!icao.getTelephonyDesignator().contains(name, Qt::CaseInsensitive)) { return false; }
196  }
197  return true;
198  });
199  }
200 
201  CAircraftModelList CAircraftModelList::findByAirlineNamesOrTelephonyDesignator(const QString &name) const
202  {
203  return this->findBy([&](const CAircraftModel &model) {
204  if (!model.hasAirlineDesignator() || !model.hasValidDbKey()) { return false; }
205  const CAirlineIcaoCode &icao = model.getAirlineIcaoCode();
206  return icao.matchesNamesOrTelephonyDesignator(name);
207  });
208  }
209 
210  CAircraftModelList CAircraftModelList::findByLiveryCode(const CLivery &livery) const
211  {
212  if (!livery.hasCombinedCode()) { return CAircraftModelList(); }
213  const QString code(livery.getCombinedCode());
214  return this->findBy([&](const CAircraftModel &model) {
215  if (!model.getLivery().hasCombinedCode()) return false;
216  return model.getLivery().getCombinedCode() == code;
217  });
218  }
219 
220  CAircraftModelList CAircraftModelList::findWithFileName() const
221  {
222  return this->findBy([](const CAircraftModel &model) { return model.hasFileName(); });
223  }
224 
225  CAircraftModelList CAircraftModelList::findByDistributor(const CDistributor &distributor) const
226  {
227  return this->findBy([&](const CAircraftModel &model) { return model.getDistributor() == distributor; });
228  }
229 
230  CAircraftModelList CAircraftModelList::findWithAircraftDesignator() const
231  {
232  return this->findBy([](const CAircraftModel &model) { return model.hasAircraftDesignator(); });
233  }
234 
235  CAircraftModelList CAircraftModelList::findWithAircraftDesignator(const QSet<QString> &designators) const
236  {
237  if (designators.isEmpty()) { return CAircraftModelList(); }
238  return this->findBy(
239  [&](const CAircraftModel &model) { return designators.contains(model.getAircraftIcaoCodeDesignator()); });
240  }
241 
242  CAircraftModelList CAircraftModelList::findWithKnownAircraftDesignator() const
243  {
244  return this->findBy([](const CAircraftModel &model) { return model.hasKnownAircraftDesignator(); });
245  }
246 
247  CAircraftModelList CAircraftModelList::findByManufacturer(const QString &manufacturer) const
248  {
249  if (manufacturer.isEmpty()) { return CAircraftModelList(); }
250  const QString m(manufacturer.toUpper().trimmed());
251  return this->findBy(
252  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getManufacturer() == m; });
253  }
254 
255  CAircraftModelList CAircraftModelList::findByFamily(const QString &family) const
256  {
257  if (family.isEmpty()) { return CAircraftModelList(); }
258  const QString f(family.toUpper().trimmed());
259  return this->findBy([&](const CAircraftModel &model) {
260  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
261  if (!icao.hasFamily()) { return false; }
262  return icao.getFamily() == f;
263  });
264  }
265 
266  CAircraftModelList CAircraftModelList::findByFamilyWithColorLivery(const QString &family) const
267  {
268  if (family.isEmpty()) { return CAircraftModelList(); }
269  const QString f(family.toUpper().trimmed());
270  return this->findBy([&](const CAircraftModel &model) {
271  if (!model.getLivery().isColorLivery()) { return false; }
272  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
273  if (!icao.hasFamily()) { return false; }
274  return icao.getFamily() == f;
275  });
276  }
277 
278  CAircraftModelList CAircraftModelList::findByDesignatorOrFamilyWithColorLivery(const CAircraftIcaoCode &icao) const
279  {
280  return this->findBy([&](const CAircraftModel &model) {
281  if (!model.getLivery().isColorLivery()) { return false; }
282  const CAircraftIcaoCode modelIcao(model.getAircraftIcaoCode());
283  if (modelIcao.getDesignator() == icao.getDesignator()) { return true; }
284  return icao.hasFamily() && modelIcao.getFamily() == icao.getFamily();
285  });
286  }
287 
289  CAircraftModelList::findByDesignatorsOrFamilyWithColorLivery(const QStringList &designators) const
290  {
291  return this->findBy([&](const CAircraftModel &model) {
292  if (!model.getLivery().isColorLivery()) { return false; }
293  const CAircraftIcaoCode modelIcao(model.getAircraftIcaoCode());
294  if (designators.contains(modelIcao.getDesignator())) { return true; }
295  return modelIcao.hasFamily() && designators.contains(modelIcao.getFamily());
296  });
297  }
298 
299  CAircraftModelList CAircraftModelList::findByCombinedType(const QString &combinedType) const
300  {
301  const QString cc(combinedType.trimmed().toUpper());
302  if (combinedType.length() != 3) { return CAircraftModelList(); }
303  return this->findBy([&](const CAircraftModel &model) {
304  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
305  return icao.matchesCombinedType(cc);
306  });
307  }
308 
309  CAircraftModelList CAircraftModelList::findByCombinedTypeAndWtc(const QString &combinedType,
310  const QString &wtc) const
311  {
312  const CAircraftModelList ml = this->findByCombinedType(combinedType);
313  if (ml.isEmpty()) { return ml; }
314  const CWakeTurbulenceCategory wtcUc =
315  wtc.isEmpty() ? CWakeTurbulenceCategory() : CWakeTurbulenceCategory(wtc.toUpper().trimmed().at(0));
316  return this->findBy([&](const CAircraftModel &model) {
317  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
318  return icao.getWtc() == wtcUc;
319  });
320  }
321 
322  CAircraftModelList CAircraftModelList::findByCombinedTypeWithColorLivery(const QString &combinedType) const
323  {
324  return this->findByCombinedType(combinedType).findColorLiveries();
325  }
326 
327  CAircraftModelList CAircraftModelList::findByCombinedTypeAndWtcWithColorLivery(const QString &combinedType,
328  const QString &wtc) const
329  {
330  return this->findByCombinedTypeAndWtc(combinedType, wtc).findColorLiveries();
331  }
332 
333  CAircraftModelList CAircraftModelList::findByCombinedAndManufacturer(const CAircraftIcaoCode &icao) const
334  {
335  return this->findByCombinedAndManufacturer(icao.getCombinedType(), icao.getManufacturer());
336  }
337 
338  CAircraftModelList CAircraftModelList::findByCombinedAndManufacturer(const QString &combinedType,
339  const QString &manufacturer) const
340  {
341  if (manufacturer.isEmpty()) { return this->findByCombinedType(combinedType); }
342  if (combinedType.isEmpty()) { return this->findByManufacturer(manufacturer); }
343  return this->findBy([&](const CAircraftModel &model) {
344  return model.getAircraftIcaoCode().matchesCombinedTypeAndManufacturer(combinedType, manufacturer);
345  });
346  }
347 
348  CAircraftModelList CAircraftModelList::findClosestColorDistance(const CRgbColor &fuselage,
349  const CRgbColor &tail) const
350  {
351  double distance = 2.0;
352  CAircraftModelList models;
353  for (const CAircraftModel &m : (*this))
354  {
355  const CLivery &l = m.getLivery();
356  if (!l.hasColorTail() || !l.hasColorFuselage()) { continue; }
357  const double d = l.getColorDistance(fuselage, tail);
358  if (qFuzzyCompare(d, distance)) { models.push_back(m); }
359  else if (distance > d)
360  {
361  models.clear();
362  models.push_back(m);
363  distance = d;
364  }
365  }
366  return models;
367  }
368 
369  CAircraftModelList CAircraftModelList::findClosestFuselageColorDistance(const CRgbColor &color) const
370  {
371  return this->findClosestColorDistance(color, color);
372  }
373 
374  CAircraftModelList CAircraftModelList::findColorLiveries() const
375  {
376  return this->findBy([=](const CAircraftModel &model) { return model.getLivery().isColorLivery(); });
377  }
378 
379  CAircraftModelList CAircraftModelList::findByMilitaryFlag(bool military) const
380  {
381  return this->findBy([=](const CAircraftModel &model) { return (model.isMilitary() == military); });
382  }
383 
384  CAircraftModelList CAircraftModelList::findByVtolFlag(bool vtol) const
385  {
386  return this->findBy([=](const CAircraftModel &model) { return (model.isVtol() == vtol); });
387  }
388 
389  CAircraftModelList CAircraftModelList::findByModelMode(CAircraftModel::ModelMode mode) const
390  {
391  return this->findBy([=](const CAircraftModel &model) { return (model.getModelMode() == mode); });
392  }
393 
394  CAircraftModelList CAircraftModelList::findByCategoryFirstLevel(int firstLevel) const
395  {
396  if (firstLevel < 0) { return CAircraftModelList(); }
397  return this->findBy([=](const CAircraftModel &model) {
398  return (model.hasCategory() && model.getAircraftIcaoCode().getCategory().getFirstLevel() == firstLevel);
399  });
400  }
401 
402  CAircraftModelList CAircraftModelList::findByCategory(const CAircraftCategory &category) const
403  {
404  if (category.isNull()) { return CAircraftModelList(); }
405  return this->findBy([=](const CAircraftModel &model) {
406  return (model.hasCategory() && model.getAircraftIcaoCode().getCategory() == category);
407  });
408  }
409 
410  CAircraftModelList CAircraftModelList::findByCategories(const CAircraftCategoryList &categories) const
411  {
412  if (categories.isEmpty()) { return CAircraftModelList(); }
413  return this->findBy([=](const CAircraftModel &model) {
414  return (model.hasCategory() && categories.contains(model.getAircraftIcaoCode().getCategory()));
415  });
416  }
417 
418  CAircraftModelList CAircraftModelList::findFsFamilyModels() const
419  {
420  return this->findBy(
421  [](const CAircraftModel &model) { return model.getSimulator().isMicrosoftOrPrepare3DSimulator(); });
422  }
423 
424  CAircraftModelList CAircraftModelList::findNonFsFamilyModels() const
425  {
426  return this->findBy(
427  [](const CAircraftModel &model) { return !model.getSimulator().isMicrosoftOrPrepare3DSimulator(); });
428  }
429 
430  CAircraftModelList CAircraftModelList::findWithValidDbKey() const
431  {
432  return this->findBy([](const CAircraftModel &model) { return model.hasValidDbKey(); });
433  }
434 
435  CAircraftModelList CAircraftModelList::findWithoutValidDbKey() const
436  {
437  return this->findBy([](const CAircraftModel &model) { return !model.hasValidDbKey(); });
438  }
439 
440  CAircraftModelList CAircraftModelList::findWithoutValidDbKey(int maxElements) const
441  {
442  CAircraftModelList ml = this->findWithoutValidDbKey();
443  ml.truncate(maxElements);
444  return ml;
445  }
446 
447  CAircraftModelList CAircraftModelList::findNonDbModelsForAirline(const QString &airline) const
448  {
449  const CAircraftModelList noDb = this->findWithoutValidDbKey();
450  return noDb.findBy(
451  [&](const CAircraftModel &model) { return model.getAirlineIcaoCode().matchesDesignator(airline); });
452  }
453 
454  CAircraftModelList CAircraftModelList::findNonDbModelsForAircraft(const QString &airline) const
455  {
456  const CAircraftModelList noDb = this->findWithoutValidDbKey();
457  return noDb.findBy(
458  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().matchesDesignator(airline); });
459  }
460 
461  CAircraftModelList CAircraftModelList::findNonDbModelsForModelString(const QString &modelString) const
462  {
463  const CAircraftModelList noDb = this->findWithoutValidDbKey();
464  return noDb.findBy(
465  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, Qt::CaseInsensitive); });
466  }
467 
468  CAircraftModelList CAircraftModelList::findAllIncludedModels() const
469  {
470  return this->findBy(
471  [](const CAircraftModel &model) { return model.getModelMode() == CAircraftModel::Include; });
472  }
473 
474  CAircraftModelList CAircraftModelList::findAllIncludedModels(int maxElements) const
475  {
476  CAircraftModelList ml = this->findAllIncludedModels();
477  ml.truncate(maxElements);
478  return ml;
479  }
480 
481  CAircraftModelList CAircraftModelList::findAllExcludedModels() const
482  {
483  return this->findBy(
484  [](const CAircraftModel &model) { return model.getModelMode() == CAircraftModel::Exclude; });
485  }
486 
487  CAircraftModelList CAircraftModelList::findAllExcludedModels(int maxElements) const
488  {
489  CAircraftModelList ml = this->findAllExcludedModels();
490  ml.truncate(maxElements);
491  return ml;
492  }
493 
494  CAircraftModelList CAircraftModelList::findDuplicateModelStrings() const
495  {
496  const QMap<QString, int> modelStrings = this->countPerModelString();
497  CAircraftModelList duplicates;
498  for (const auto [string, count] : makePairsRange(modelStrings))
499  {
500  if (count > 1) { duplicates.push_back(this->findByModelString(string, Qt::CaseInsensitive)); }
501  }
502  return duplicates;
503  }
504 
505  QMap<QString, int> CAircraftModelList::countPerModelString() const
506  {
507  QMap<QString, int> modelStrings;
508  for (const CAircraftModel &model : *this)
509  {
510  if (modelStrings.contains(model.getModelString())) { modelStrings[model.getModelModeAsString()]++; }
511  else { modelStrings[model.getModelModeAsString()] = 1; }
512  }
513  return modelStrings;
514  }
515 
516  QMap<CDistributor, int> CAircraftModelList::countPerDistributor() const
517  {
518  QMap<CDistributor, int> distributors;
519  for (const CAircraftModel &model : *this)
520  {
521  if (!model.hasDistributor()) { continue; }
522  if (distributors.contains(model.getDistributor())) { distributors[model.getDistributor()]++; }
523  else { distributors[model.getDistributor()] = 1; }
524  }
525  return distributors;
526  }
527 
528  QMap<CAircraftIcaoCode, int> CAircraftModelList::countPerAircraftIcao() const
529  {
531  for (const CAircraftModel &model : *this)
532  {
533  if (!model.hasAircraftDesignator()) { continue; }
534  if (icaos.contains(model.getAircraftIcaoCode())) { icaos[model.getAircraftIcaoCode()]++; }
535  else { icaos[model.getAircraftIcaoCode()] = 1; }
536  }
537  return icaos;
538  }
539 
540  QMap<CAirlineIcaoCode, int> CAircraftModelList::countPerAirlineIcao() const
541  {
543  for (const CAircraftModel &model : *this)
544  {
545  if (!model.hasAirlineDesignator()) { continue; }
546  if (icaos.contains(model.getAirlineIcaoCode())) { icaos[model.getAirlineIcaoCode()]++; }
547  else { icaos[model.getAirlineIcaoCode()] = 1; }
548  }
549  return icaos;
550  }
551 
552  CAirlineIcaoCode CAircraftModelList::getAirlineWithMaxCount() const
553  {
554  const QMap<CAirlineIcaoCode, int> ac = this->countPerAirlineIcao();
555  if (ac.isEmpty()) { return {}; }
556  if (ac.size() == 1) { return ac.firstKey(); }
557  const QList<int> values = ac.values();
558  const int max = *std::max_element(values.begin(), values.end());
559  return ac.key(max);
560  }
561 
562  CAircraftModelList CAircraftModelList::findModelsWithoutExistingFile() const
563  {
564  return this->findBy([](const CAircraftModel &model) { return !model.hasExistingCorrespondingFile(); });
565  }
566 
567  CAircraftModelList CAircraftModelList::findModelsWithExistingFile() const
568  {
569  return this->findBy([](const CAircraftModel &model) { return model.hasExistingCorrespondingFile(); });
570  }
571 
572  QString CAircraftModelList::designatorToFamily(const CAircraftIcaoCode &aircraftIcaoCode) const
573  {
574  if (aircraftIcaoCode.hasFamily()) { return aircraftIcaoCode.getFamily(); }
575  for (const CAircraftModel &model : (*this))
576  {
577  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
578  if (!icao.hasFamily()) continue;
579  if (icao.matchesDesignator(aircraftIcaoCode.getDesignator())) { return icao.getFamily(); }
580  }
581  return QString();
582  }
583 
584  CAircraftModelList CAircraftModelList::matchesSimulator(const CSimulatorInfo &simulator) const
585  {
586  return this->findBy([&](const CAircraftModel &model) { return model.matchesSimulator(simulator); });
587  }
588 
589  bool CAircraftModelList::containsMatchingSimulator(const CSimulatorInfo &simulators) const
590  {
591  return this->containsBy([&](const CAircraftModel &model) { return model.matchesSimulator(simulators); });
592  }
593 
594  bool CAircraftModelList::containsNotMatchingSimulator(const CSimulatorInfo &simulators) const
595  {
596  return this->containsBy([&](const CAircraftModel &model) { return !model.matchesSimulator(simulators); });
597  }
598 
599  bool CAircraftModelList::containsMilitary() const
600  {
601  return this->containsBy([&](const CAircraftModel &model) { return model.isMilitary(); });
602  }
603 
604  bool CAircraftModelList::containsCivilian() const
605  {
606  return this->containsBy([&](const CAircraftModel &model) { return model.isCivilian(); });
607  }
608 
609  bool CAircraftModelList::containsCivilianAndMilitary() const
610  {
611  return this->containsMilitary() && this->containsCivilian();
612  }
613 
614  bool CAircraftModelList::containsVtol() const
615  {
616  return this->containsBy([&](const CAircraftModel &model) { return model.isVtol(); });
617  }
618 
619  bool CAircraftModelList::containsCategory() const
620  {
621  return this->containsBy([&](const CAircraftModel &model) { return model.hasCategory(); });
622  }
623 
624  bool CAircraftModelList::containsCategory(int firstLevel) const
625  {
626  if (firstLevel < 0) { return false; }
627  return this->containsBy([&](const CAircraftModel &model) {
628  return model.hasCategory() && model.getAircraftIcaoCode().getCategory().getFirstLevel() == firstLevel;
629  });
630  }
631 
632  CAircraftModelList CAircraftModelList::findByDistributors(const CDistributorList &distributors) const
633  {
634  if (distributors.isEmpty()) { return CAircraftModelList(); }
635  return this->findBy([&](const CAircraftModel &model) { return model.matchesAnyDbDistributor(distributors); });
636  }
637 
638  int CAircraftModelList::setSimulatorInfo(const CSimulatorInfo &info)
639  {
640  int c = 0;
641  const CSimulatorInfo::Simulator s = info.getSimulator();
642  for (CAircraftModel &model : (*this))
643  {
644  if (model.getSimulator().getSimulator() == s) { continue; }
645  model.setSimulator(info);
646  c++;
647  }
648  return c;
649  }
650 
651  CSimulatorInfo CAircraftModelList::simulatorsSupported() const
652  {
653  CSimulatorInfo::Simulator s = CSimulatorInfo::None;
654  for (const CAircraftModel &model : (*this))
655  {
656  s |= model.getSimulator().getSimulator();
657  if (s == CSimulatorInfo::All) { break; }
658  }
659  return CSimulatorInfo(s);
660  }
661 
662  namespace private_ns
663  {
664  bool isLikelyImpl(double count, double total)
665  {
666  if (total < 1) { return false; }
667  const double fsRatio = count / total;
668  return fsRatio > 0.95;
669  }
670  } // namespace private_ns
671 
672  bool CAircraftModelList::isLikelyFsFamilyModelList() const
673  {
674  if (this->isEmpty()) { return false; } // avoid DIV 0
675  return private_ns::isLikelyImpl(this->countPerSimulator().getCountForFsFamilySimulators(), this->size());
676  }
677 
678  bool CAircraftModelList::isLikelyFsxFamilyModelList() const
679  {
680  if (this->isEmpty()) { return false; } // avoid DIV 0
681  return private_ns::isLikelyImpl(this->countPerSimulator().getCountForFsxFamilySimulators(), this->size());
682  }
683 
684  bool CAircraftModelList::isLikelyXPlaneModelList() const
685  {
686  if (this->isEmpty()) { return false; } // avoid DIV 0
687  return private_ns::isLikelyImpl(this->countPerSimulator().getCount(CSimulatorInfo::xplane()), this->size());
688  }
689 
690  int CAircraftModelList::setModelMode(CAircraftModel::ModelMode mode)
691  {
692  int c = 0;
693  for (CAircraftModel &model : (*this))
694  {
695  if (model.getModelMode() == mode) { continue; }
696  model.setModelMode(mode);
697  c++;
698  }
699  return c;
700  }
701 
702  int CAircraftModelList::setModelType(CAircraftModel::ModelType type)
703  {
704  int c = 0;
705  for (CAircraftModel &model : (*this))
706  {
707  if (model.getModelType() == type) { continue; }
708  model.setModelType(type);
709  c++;
710  }
711  return c;
712  }
713 
714  int CAircraftModelList::setCG(const CLength &cg)
715  {
716  int c = 0;
717  for (CAircraftModel &model : (*this))
718  {
719  if (model.getCG() == cg) { continue; }
720  model.setCG(cg);
721  c++;
722  }
723  return c;
724  }
725 
726  int CAircraftModelList::keepModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity)
727  {
728  const int cs = this->size();
729  (*this) = (findByModelStrings(modelStrings, sensitivity));
730  const int d = cs - this->size();
731  return d;
732  }
733 
734  bool CAircraftModelList::removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity)
735  {
736  if (modelString.isEmpty()) { return false; }
737  if (this->isEmpty()) { return false; }
738  const int r = this->removeIf(
739  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
740  return r > 0;
741  }
742 
743  int CAircraftModelList::removeModelsWithString(const CAircraftModelList &models, Qt::CaseSensitivity sensitivity)
744  {
745  if (models.isEmpty()) { return 0; }
746  return this->removeModelsWithString(models.getModelStringList(false), sensitivity);
747  }
748 
749  int CAircraftModelList::removeModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity)
750  {
751  if (modelStrings.isEmpty()) { return 0; }
752  const int cs = this->size();
753  (*this) = (this->findByNotInModelStrings(modelStrings, sensitivity));
754  const int d = cs - this->size();
755  return d;
756  }
757 
758  int CAircraftModelList::removeIfNotMatchingSimulator(const CSimulatorInfo &needToMatch)
759  {
760  if (this->isEmpty()) { return 0; }
761  const int oldSize = this->size();
762  CAircraftModelList models;
763  for (const CAircraftModel &model : *this)
764  {
765  if (model.matchesSimulator(needToMatch)) { models.push_back(model); }
766  }
767  const int diff = models.size() - oldSize;
768  if (diff > 0) { *this = models; }
769  return diff;
770  }
771 
772  int CAircraftModelList::removeAllWithoutModelString()
773  {
774  if (this->isEmpty()) { return 0; }
775  const int s = this->size();
776  CAircraftModelList withModelStr;
777  for (const CAircraftModel &model : *this)
778  {
779  if (!model.hasModelString()) { continue; }
780  withModelStr.push_back(model);
781  }
782  const int diff = s - withModelStr.size();
783  if (diff < 1) { return 0; }
784  *this = withModelStr;
785  return diff;
786  }
787 
788  int CAircraftModelList::removeIfExcluded()
789  {
790  if (this->isEmpty()) { return 0; }
791  const int s = this->size();
792  CAircraftModelList onlyIncluded;
793  for (const CAircraftModel &model : *this)
794  {
795  if (model.getModelMode() == CAircraftModel::Exclude) { continue; }
796  onlyIncluded.push_back(model);
797  }
798  const int diff = s - onlyIncluded.size();
799  if (diff < 1) { return 0; }
800  *this = onlyIncluded;
801  return diff;
802  }
803 
804  int CAircraftModelList::removeXPlaneFlyablePlanes() { return this->removeIfExcluded(); }
805 
806  int CAircraftModelList::removeByDistributor(const CDistributor &distributor)
807  {
808  return this->removeIf(&CAircraftModel::getDistributor, distributor);
809  }
810 
811  int CAircraftModelList::removeByAircraftAndLivery(const CAircraftIcaoCode &aircraftIcao, const CLivery &livery)
812  {
813  return this->removeIf(&CAircraftModel::getAircraftIcaoCode, aircraftIcao, &CAircraftModel::getLivery, livery);
814  }
815 
816  int CAircraftModelList::removeByAircraftAndAirline(const CAircraftIcaoCode &aircraftIcao,
817  const CAirlineIcaoCode &airline)
818  {
819  return this->removeIf(&CAircraftModel::getAircraftIcaoCode, aircraftIcao, &CAircraftModel::getAirlineIcaoCode,
820  airline);
821  }
822 
823  int CAircraftModelList::removeIfNotFsFamily()
824  {
825  if (this->isEmpty()) { return 0; }
826  CAircraftModelList fsOnly = this->findFsFamilyModels();
827  if (fsOnly.size() == this->size()) { return 0; }
828  const int delta = this->size() - fsOnly.size();
829  *this = fsOnly;
830  return delta;
831  }
832 
833  CAircraftModelList CAircraftModelList::removeIfFileButNotInSet(const QString &fileName,
834  const QSet<QString> &modelStrings)
835  {
836  CAircraftModelList removed;
837  for (const CAircraftModel &model : *this)
838  {
839  if (!model.matchesFileName(fileName)) { continue; }
840  if (modelStrings.contains(model.getModelString())) { continue; }
841  removed.push_back(model);
842  }
843 
844  this->removeIfIn(removed);
845  return removed;
846  }
847 
848  bool CAircraftModelList::replaceOrAddModelWithString(const CAircraftModel &addOrReplaceModel,
849  Qt::CaseSensitivity sensitivity)
850  {
851  bool r = false;
852  if (!this->isEmpty()) { r = this->removeModelWithString(addOrReplaceModel.getModelString(), sensitivity); }
853  this->push_back(addOrReplaceModel);
854  return r;
855  }
856 
857  int CAircraftModelList::replaceOrAddModelsWithString(const CAircraftModelList &addOrReplaceList,
858  Qt::CaseSensitivity sensitivity)
859  {
860  if (addOrReplaceList.isEmpty()) { return 0; }
861  if (this->isEmpty())
862  {
863  *this = addOrReplaceList;
864  return addOrReplaceList.size();
865  }
866  CAircraftModelList newModels(*this);
867  const QStringList keys(addOrReplaceList.getModelStringList(false));
868  newModels.removeModelsWithString(keys, sensitivity);
869  int removed = newModels.size(); // size after removing
870  newModels.push_back(addOrReplaceList);
871  *this = newModels;
872  return this->size() - removed;
873  }
874 
875  CAircraftModelList CAircraftModelList::findModelsStartingWith(const QString &modelString,
876  Qt::CaseSensitivity sensitivity) const
877  {
878  return this->findBy(
879  [&](const CAircraftModel &model) { return model.getModelString().startsWith(modelString, sensitivity); });
880  }
881 
882  CAircraftModelList CAircraftModelList::findByModelStrings(const QStringList &modelStrings,
883  Qt::CaseSensitivity sensitivity) const
884  {
885  return this->findBy(
886  [&](const CAircraftModel &model) { return modelStrings.contains(model.getModelString(), sensitivity); });
887  }
888 
889  CAircraftModelList CAircraftModelList::findByNotInModelStrings(const QStringList &modelStrings,
890  Qt::CaseSensitivity sensitivity) const
891  {
892  return this->findBy([&](const CAircraftModel &model) {
893  const bool c = modelStrings.contains(model.getModelString(), sensitivity);
894  return !c;
895  });
896  }
897 
898  QStringList CAircraftModelList::getModelStringList(bool sort) const
899  {
900  QStringList ms;
901  for (const CAircraftModel &model : *this)
902  {
903  if (!model.hasModelString()) { continue; }
904  ms.append(model.getModelString());
905  }
906  if (sort) { ms.sort(Qt::CaseInsensitive); }
907  return ms;
908  }
909 
910  QSet<QString> CAircraftModelList::getModelStringSet() const
911  {
913  for (const CAircraftModel &model : *this)
914  {
915  if (!model.hasModelString()) { continue; }
916  ms.insert(model.getModelString());
917  }
918  return ms;
919  }
920 
921  CCountPerSimulator CAircraftModelList::countPerSimulator() const
922  {
923  CCountPerSimulator count;
924  for (const CAircraftModel &model : *this) { count.increaseSimulatorCounts(model.getSimulator()); }
925  return count;
926  }
927 
928  CSimulatorInfo CAircraftModelList::simulatorsWithMaxEntries() const
929  {
930  if (this->isEmpty()) { return CSimulatorInfo(); } // not known
931  const CCountPerSimulator counts(this->countPerSimulator());
932  const int simulatorsRepresented = counts.simulatorsRepresented();
933  if (simulatorsRepresented < 1) { return CSimulatorInfo(); }
934  const QMultiMap<int, CSimulatorInfo> cps(counts.countPerSimulator());
935  CSimulatorInfo maxSim = cps.last();
936  const int count = cps.lastKey(); // how many elements
937  const QList<CSimulatorInfo> infoWithMaxValues = cps.values(count); // all with the same counts
938  for (const CSimulatorInfo &info : infoWithMaxValues) { maxSim.addSimulator(info); }
939  return maxSim;
940  }
941 
942  int CAircraftModelList::countModelsWithColorLivery() const
943  {
944  int count = 0;
945  for (const CAircraftModel &model : *this)
946  {
947  if (model.getLivery().isColorLivery()) { count++; }
948  }
949  return count;
950  }
951 
952  int CAircraftModelList::countModelsWithAirlineLivery() const
953  {
954  int count = 0;
955  for (const CAircraftModel &model : *this)
956  {
957  if (model.getLivery().isAirlineLivery()) { count++; }
958  }
959  return count;
960  }
961 
962  int CAircraftModelList::countVtolAircraft() const
963  {
964  int count = 0;
965  for (const CAircraftModel &model : *this)
966  {
967  if (model.isVtol()) { count++; }
968  }
969  return count;
970  }
971 
972  int CAircraftModelList::countByMode(CAircraftModel::ModelMode mode) const
973  {
974  int count = 0;
975  for (const CAircraftModel &model : *this)
976  {
977  if (model.matchesMode(mode)) { count++; }
978  }
979  return count;
980  }
981 
982  int CAircraftModelList::countMilitaryAircraft() const
983  {
984  int count = 0;
985  for (const CAircraftModel &model : *this)
986  {
987  if (model.isMilitary()) { count++; }
988  }
989  return count;
990  }
991 
992  int CAircraftModelList::countCivilianAircraft() const
993  {
994  int count = 0;
995  for (const CAircraftModel &model : (*this))
996  {
997  if (model.isCivilian()) { count++; }
998  }
999  return count;
1000  }
1001 
1002  int CAircraftModelList::countDifferentAirlines() const { return this->getAirlineVDesignators().size(); }
1003 
1004  int CAircraftModelList::countCombinedTypes() const { return this->getCombinedTypes().size(); }
1005 
1006  int CAircraftModelList::countAliases() const
1007  {
1008  int count = 0;
1009  for (const CAircraftModel &model : (*this))
1010  {
1011  if (model.hasModelStringAlias()) { count++; }
1012  }
1013  return count;
1014  }
1015 
1016  void CAircraftModelList::sortByFileName()
1017  {
1018  if (CFileUtils::isFileNameCaseSensitive()) { this->sortBy(&CAircraftModel::getFileName); }
1019  else { this->sortBy(&CAircraftModel::getFileNameLowerCase); }
1020  }
1021 
1022  void CAircraftModelList::updateDistributor(const CDistributor &distributor)
1023  {
1024  for (CAircraftModel &model : *this) { model.setDistributor(distributor); }
1025  }
1026 
1027  CDistributorList CAircraftModelList::getDistributors(bool onlyDbDistributors) const
1028  {
1029  if (this->isEmpty()) { return CDistributorList(); }
1030  CDistributorList distributors;
1031  for (const CAircraftModel &model : *this)
1032  {
1033  const CDistributor d(model.getDistributor());
1034  if (onlyDbDistributors && !d.hasValidDbKey()) { continue; }
1035  if (distributors.contains(d)) { continue; }
1036  distributors.push_back(d);
1037  }
1038  return distributors;
1039  }
1040 
1041  CAircraftIcaoCodeList CAircraftModelList::getAircraftIcaoCodesFromDb() const
1042  {
1043  if (this->isEmpty()) { return CAircraftIcaoCodeList(); }
1044  QSet<int> keys;
1045  CAircraftIcaoCodeList icaos;
1046  for (const CAircraftModel &model : *this)
1047  {
1048  const CAircraftIcaoCode icao = model.getAircraftIcaoCode();
1049  if (!icao.hasValidDbKey()) { continue; }
1050 
1051  const int key = icao.getDbKey();
1052  if (keys.contains(key)) { continue; }
1053  icaos.push_back(icao);
1054  keys.insert(key);
1055  }
1056  return icaos;
1057  }
1058 
1059  QSet<QString> CAircraftModelList::getAircraftDesignators() const
1060  {
1061  CSetBuilder<QString> designators;
1062  for (const CAircraftModel &model : *this)
1063  {
1064  if (!model.hasAircraftDesignator()) { continue; }
1065  designators.insert(model.getAircraftIcaoCodeDesignator());
1066  }
1067  return designators;
1068  }
1069 
1070  QSet<QString> CAircraftModelList::getAircraftDesignatorsForAirline(const CAirlineIcaoCode &airlineCode) const
1071  {
1072  CSetBuilder<QString> designators;
1073  if (!airlineCode.hasValidDesignator()) { return {}; }
1074  for (const CAircraftModel &model : *this)
1075  {
1076  if (model.getAirlineIcaoCode() != airlineCode) { continue; }
1077  designators.insert(model.getAircraftIcaoCodeDesignator());
1078  }
1079  return designators;
1080  }
1081 
1082  CAircraftIcaoCodeList CAircraftModelList::getAicraftIcaoCodesForAirline(const CAirlineIcaoCode &airlineCode) const
1083  {
1084  CAircraftIcaoCodeList icaos;
1085  if (!airlineCode.hasValidDesignator()) { return icaos; }
1086  for (const CAircraftModel &model : *this)
1087  {
1088  if (model.getAirlineIcaoCode() != airlineCode) { continue; }
1089  icaos.push_back(model.getAircraftIcaoCode());
1090  }
1091  return icaos;
1092  }
1093 
1094  CAirlineIcaoCodeList CAircraftModelList::getAirlineIcaoCodesFromDb() const
1095  {
1096  if (this->isEmpty()) { return CAirlineIcaoCodeList(); }
1097  QSet<int> keys;
1098  CAirlineIcaoCodeList icaos;
1099  for (const CAircraftModel &model : *this)
1100  {
1101  const CAirlineIcaoCode icao = model.getAirlineIcaoCode();
1102  if (!icao.hasValidDbKey()) { continue; }
1103 
1104  const int key = icao.getDbKey();
1105  if (keys.contains(key)) { continue; }
1106  icaos.push_back(icao);
1107  keys.insert(key);
1108  }
1109  return icaos;
1110  }
1111 
1112  QSet<QString> CAircraftModelList::getAirlineDesignators() const
1113  {
1114  CSetBuilder<QString> designators;
1115  for (const CAircraftModel &model : *this)
1116  {
1117  if (!model.hasAirlineDesignator()) { continue; }
1118  designators.insert(model.getAirlineIcaoCodeDesignator());
1119  }
1120  return designators;
1121  }
1122 
1123  QSet<QString> CAircraftModelList::getAirlineVDesignators() const
1124  {
1125  CSetBuilder<QString> designators;
1126  for (const CAircraftModel &model : *this)
1127  {
1128  if (!model.hasAirlineDesignator()) { continue; }
1129  designators.insert(model.getAirlineIcaoCodeVDesignator());
1130  }
1131  return designators;
1132  }
1133 
1134  CAirlineIcaoCodeList CAircraftModelList::getAirlineIcaoCodesForGroup(int groupId) const
1135  {
1136  if (groupId < 0) { return {}; }
1137  CAirlineIcaoCodeList icaos;
1138  for (const CAircraftModel &model : *this)
1139  {
1140  if (model.getAirlineIcaoCode().getGroupId() == groupId) { icaos.push_back(model.getAirlineIcaoCode()); }
1141  }
1142  return icaos;
1143  }
1144 
1145  QSet<QString> CAircraftModelList::getAirlineDesignatorsForGroup(int groupId) const
1146  {
1147  return this->getAirlineIcaoCodesForGroup(groupId).allDesignators();
1148  }
1149 
1150  QSet<QString> CAircraftModelList::getAirlineVDesignatorsForGroup(int groupId) const
1151  {
1152  return this->getAirlineIcaoCodesForGroup(groupId).allVDesignators();
1153  }
1154 
1155  QSet<QString> CAircraftModelList::getCombinedTypes() const
1156  {
1157  CSetBuilder<QString> combinedCodes;
1158  for (const CAircraftModel &model : *this)
1159  {
1160  const QString ct = model.getAircraftIcaoCode().getCombinedType();
1161  if (ct.isEmpty()) { continue; }
1162  combinedCodes.insert(ct);
1163  }
1164  return combinedCodes;
1165  }
1166 
1167  QSet<QString> CAircraftModelList::getAllFileNames() const
1168  {
1169  const bool cs = CFileUtils::isFileNameCaseSensitive();
1170  CSetBuilder<QString> files;
1171  for (const CAircraftModel &model : *this)
1172  {
1173  if (!model.hasFileName()) { continue; }
1174  files.insert(cs ? model.getFileName() : model.getFileNameLowerCase());
1175  }
1176  return files;
1177  }
1178 
1179  QSet<QString> CAircraftModelList::getAllUNCFileNames() const
1180  {
1181  const bool cs = CFileUtils::isFileNameCaseSensitive();
1182  CSetBuilder<QString> files;
1183  for (const CAircraftModel &model : *this)
1184  {
1185  if (!model.hasFileName()) { continue; }
1186  const QString fn = (cs ? model.getFileName() : model.getFileNameLowerCase());
1187  if (CFileUtils::isWindowsUncPath(fn)) { files.insert(fn); }
1188  }
1189  return files;
1190  }
1191 
1192  QString CAircraftModelList::getCombinedTypesAsString(const QString &separator) const
1193  {
1194  if (this->isEmpty()) { return {}; }
1195  return joinStringSet(this->getCombinedTypes(), separator);
1196  }
1197 
1198  QSet<QString> CAircraftModelList::getAicraftAndAirlineDesignators(bool withDbId) const
1199  {
1201  for (const CAircraftModel &model : *this)
1202  {
1203  const QString s =
1204  (model.hasAircraftDesignator() ? (withDbId ? model.getAircraftIcaoCode().getDesignatorDbKey() :
1206  "no aircraft") %
1207  u"/" %
1208  (model.hasAircraftDesignator() ? (withDbId ? model.getAirlineIcaoCode().getDesignatorDbKey() :
1210  "no airline");
1211  str.insert(s);
1212  }
1213  return str;
1214  }
1215 
1216  QString CAircraftModelList::getAicraftAndAirlineDesignatorsAsString(bool withDbId, const QString &separator) const
1217  {
1218  if (this->isEmpty()) { return {}; }
1219  return joinStringSet(this->getAicraftAndAirlineDesignators(withDbId), separator);
1220  }
1221 
1222  void CAircraftModelList::updateAircraftIcao(const CAircraftIcaoCode &icao)
1223  {
1224  for (CAircraftModel &model : *this) { model.setAircraftIcaoCode(icao); }
1225  }
1226 
1227  void CAircraftModelList::updateLivery(const CLivery &livery)
1228  {
1229  for (CAircraftModel &model : *this) { model.setLivery(livery); }
1230  }
1231 
1232  int CAircraftModelList::updateDistributorOrder(const CDistributorList &distributors)
1233  {
1234  if (distributors.isEmpty()) { return 0; }
1235  int found = 0;
1236  for (CAircraftModel &model : *this)
1237  {
1238  if (model.setDistributorOrder(distributors)) { found++; }
1239  }
1240  return found;
1241  }
1242 
1243  void CAircraftModelList::normalizeFileNamesForDb()
1244  {
1245  for (CAircraftModel &model : *this) { model.normalizeFileNameForDb(); }
1246  }
1247 
1248  ScoredModels CAircraftModelList::scoreFull(const CAircraftModel &remoteModel, bool preferColorLiveries,
1249  bool ignoreZeroScores, CStatusMessageList *log) const
1250  {
1251  ScoredModels scoreMap;
1252 
1253  // normally prefer colors if there is no airline
1254  CCallsign::addLogDetailsToList(
1255  log, remoteModel.getCallsign(),
1256  QStringLiteral("Prefer color liveries: '%1', airline: '%2', ignore zero scores: '%3'")
1257  .arg(boolToYesNo(preferColorLiveries), remoteModel.getAirlineIcaoCodeDesignator(),
1258  boolToYesNo(ignoreZeroScores)));
1259  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(),
1260  QStringLiteral("--- Start scoring in list with %1 models").arg(this->size()));
1261  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(), this->coverageSummaryForModel(remoteModel));
1262 
1263  int c = 1;
1264  for (const CAircraftModel &model : *this)
1265  {
1266  CStatusMessageList subMsgs;
1267  const int score = model.calculateScore(remoteModel, preferColorLiveries, log ? &subMsgs : nullptr);
1268  if (ignoreZeroScores && score < 1) { continue; }
1269 
1270  CCallsign::addLogDetailsToList(
1271  log, remoteModel.getCallsign(),
1272  QStringLiteral("--- Calculating #%1 '%2'---").arg(c).arg(model.getModelStringAndDbKey()));
1273  if (log) { log->push_back(subMsgs); }
1274  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(),
1275  QStringLiteral("--- End calculating #%1 ---").arg(c));
1276  c++;
1277  scoreMap.insert(score, model);
1278  }
1279  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(), QStringLiteral("--- End scoring ---"));
1280  return scoreMap;
1281  }
1282 
1283  QStringList CAircraftModelList::toCompleterStrings(bool sorted, const CSimulatorInfo &simulator) const
1284  {
1285  QStringList c;
1286  for (const CAircraftModel &model : *this)
1287  {
1288  if (model.getSimulator().matchesAny(simulator)) { c.append(model.getModelString()); }
1289  }
1290  if (sorted) { c.sort(); }
1291  return c;
1292  }
1293 
1294  CStatusMessageList CAircraftModelList::validateForPublishing() const
1295  {
1296  CAircraftModelList invalidModels;
1297  CAircraftModelList validModels;
1298  return this->validateForPublishing(validModels, invalidModels);
1299  }
1300 
1301  CStatusMessageList CAircraftModelList::validateForPublishing(CAircraftModelList &validModels,
1302  CAircraftModelList &invalidModels) const
1303  {
1304  if (this->isEmpty()) { return CStatusMessageList(); }
1305  CStatusMessageList msgs;
1306  for (const CAircraftModel &model : *this)
1307  {
1308  const CStatusMessageList msgsModel(model.validate(false));
1309  const CStatusMessage msgModel(msgsModel.toSingleMessage());
1310 
1311  QStringList subMsgs;
1312  if (!model.getDistributor().hasValidDbKey()) { subMsgs << "No distributor from DB"; }
1313  if (!model.getAircraftIcaoCode().hasValidDbKey()) { subMsgs << "No aircraft ICAO from DB"; }
1314  if (!model.getLivery().hasValidDbKey()) { subMsgs << "No livery from DB"; }
1315  if (model.getLivery().isAirlineLivery())
1316  {
1317  // for color codes we do not need to check
1318  if (!model.getLivery().getAirlineIcaoCode().hasValidDbKey()) { subMsgs << "No airline ICAO from DB"; }
1319  }
1320 
1321  const CStatusMessage msgDb(CStatusMessage::SeverityError, subMsgs.join(", "));
1322  CStatusMessage singleMsg(CStatusMessageList({ msgModel, msgDb }).toSingleMessage());
1323  if (!singleMsg.isWarningOrAbove())
1324  {
1325  CAircraftModelList::addAsValidOrInvalidModel(model, true, validModels, invalidModels);
1326  continue;
1327  }
1328  if (model.hasModelString()) { singleMsg.prependMessage(model.getModelString() % u": "); }
1329  msgs.push_back(singleMsg);
1330  CAircraftModelList::addAsValidOrInvalidModel(model, false, validModels, invalidModels);
1331  }
1332  return msgs;
1333  }
1334 
1335  CStatusMessageList CAircraftModelList::validateDistributors(const CDistributorList &distributors,
1336  CAircraftModelList &validModels,
1337  CAircraftModelList &invalidModels) const
1338  {
1339  CStatusMessageList msgs;
1340  CDistributorList distributorsFromDb(distributors);
1341  distributorsFromDb.removeIfNotLoadedFromDb();
1342 
1343  // Any DB distributors?
1344  if (distributorsFromDb.isEmpty())
1345  {
1346  const CStatusMessage msg = CStatusMessage(this).validationError(u"No DB distributors for validation");
1347  msgs.push_back(msg);
1348  CAircraftModelList::addAsValidOrInvalidModels(*this, false, validModels, invalidModels);
1349  return msgs;
1350  }
1351 
1352  for (const CAircraftModel &model : *this)
1353  {
1354  const bool valid = (model.hasDbDistributor() || model.matchesAnyDbDistributor(distributorsFromDb));
1355  CAircraftModelList::addAsValidOrInvalidModel(model, valid, validModels, invalidModels);
1356  if (!valid)
1357  {
1358  const CStatusMessage msg =
1359  CStatusMessage(this).validationError(u"No valid distributor for '%1', was '%2'")
1360  << model.getModelString() << model.getDistributor().getDbKey();
1361  msgs.push_back(msg);
1362  }
1363  }
1364  return msgs;
1365  }
1366 
1367  CStatusMessageList CAircraftModelList::validateFiles(CAircraftModelList &validModels,
1368  CAircraftModelList &invalidModels, bool ignoreEmptyFileNames,
1369  int stopAtFailedFiles, std::atomic_bool &wasStopped,
1370  const QString &simRootDirectory, bool alreadySortedByFn) const
1371  {
1372  wasStopped = false;
1373 
1374  CStatusMessageList msgs;
1375  QSet<QString> failedFiles;
1376  QSet<QString> workingFiles;
1377  int failedFilesCount = 0;
1378 
1379  // sorting allows to skip multiple files as once when a file fails
1380  CAircraftModelList sorted(*this);
1381  if (!alreadySortedByFn) { sorted.sortByFileName(); }
1382 
1383  // avoid hanging if UNC paths are not available
1384  if (CBuildConfig::isRunningOnWindowsNtPlatform())
1385  {
1386  const CStatusMessageList uncMsgs = this->validateUncFiles(sorted.getAllUNCFileNames());
1387  if (uncMsgs.hasErrorMessages()) { return uncMsgs; }
1388  }
1389 
1390  const bool caseSensitive = CFileUtils::isFileNameCaseSensitive();
1391  const QString simRootDir = CFileUtils::normalizeFilePathToQtStandard(
1392  CFileUtils::stripLeadingSlashOrDriveLetter(caseSensitive ? simRootDirectory : simRootDirectory.toLower()));
1393 
1394  for (const CAircraftModel &model : std::as_const(sorted))
1395  {
1396  if (wasStopped) { break; } // allow breaking from external
1397  bool ok = false;
1398  do {
1399  if (!model.hasModelString())
1400  {
1401  msgs.push_back(CStatusMessage(this).validationError(u"No model string"));
1402  break;
1403  }
1404 
1405  if (!model.hasFileName())
1406  {
1407  if (ignoreEmptyFileNames) { continue; }
1408  msgs.push_back(CStatusMessage(this).validationError(u"'%1', no file name")
1409  << model.getModelStringAndDbKey());
1410  break;
1411  }
1412 
1413  const QString fn(caseSensitive ? model.getFileName() : model.getFileNameLowerCase());
1414  if (failedFiles.contains(fn))
1415  {
1416  msgs.push_back(CStatusMessage(this).validationError(u"'%1', known failed file '%2' skipped")
1417  << model.getModelStringAndDbKey() << model.getFileName());
1418  break;
1419  }
1420 
1421  if (workingFiles.contains(fn) || model.hasExistingCorrespondingFile())
1422  {
1423  if (!simRootDirectory.isEmpty() && !fn.contains(simRootDir))
1424  {
1425  // check if in root directory
1426  msgs.push_back(
1427  CStatusMessage(this).validationError(u"'%1', not in root directory '%2', '%3' skipped")
1428  << model.getModelStringAndDbKey() << simRootDir << model.getFileName());
1429  failedFiles.insert(fn);
1430  failedFilesCount++;
1431  break;
1432  }
1433  else
1434  {
1435  ok = true;
1436  workingFiles.insert(fn);
1437  // msgs.push_back(CStatusMessage(this).validationInfo(u"'%1', file '%2' existing") <<
1438  // model.getModelStringAndDbKey() << model.getFileName());
1439  break;
1440  }
1441  }
1442 
1443  failedFiles.insert(fn);
1444  failedFilesCount++;
1445  msgs.push_back(CStatusMessage(this).validationError(u"'%1', file '%2' not existing")
1446  << model.getModelStringAndDbKey() << model.getFileName());
1447  }
1448  while (false);
1449 
1450  CAircraftModelList::addAsValidOrInvalidModel(model, ok, validModels, invalidModels);
1451  if (stopAtFailedFiles > 0 && failedFilesCount >= stopAtFailedFiles)
1452  {
1453  wasStopped = true;
1454  msgs.push_back(CStatusMessage(this).validationWarning(u"Stopping after %1 failed files")
1455  << failedFilesCount);
1456  break;
1457  }
1458  }
1459 
1460  // Summary
1461  if (!validModels.isEmpty())
1462  {
1463  msgs.push_back(CStatusMessage(this).validationInfo(u"File validation, valid models: %1")
1464  << validModels.size());
1465  }
1466  if (!invalidModels.isEmpty())
1467  {
1468  msgs.push_back(CStatusMessage(this).validationWarning(u"File validation, invalid models: %1")
1469  << invalidModels.size());
1470  }
1471 
1472  // done
1473  return msgs;
1474  }
1475 
1476  CStatusMessageList CAircraftModelList::validateUncFiles(const QSet<QString> &uncFiles) const
1477  {
1478  // check if UNC paths can be reached
1479  CStatusMessageList msgs;
1480  if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return msgs; }
1481  if (uncFiles.isEmpty()) { return msgs; }
1482 
1483  const QSet<QString> uncMachines = CFileUtils::windowsUncMachines(uncFiles);
1484  if (uncMachines.isEmpty())
1485  {
1486  msgs.push_back(CStatusMessage(this).validationInfo(u"Found NO UNC machines for %1 files, odd...?")
1487  << uncFiles.size());
1488  }
1489  else
1490  {
1491  const QString machines = joinStringSet(uncMachines, ", ");
1492  msgs.push_back(CStatusMessage(this).validationInfo(u"Found %1 UNC files on machines: %2")
1493  << uncFiles.size() << machines);
1494  }
1495 
1496  for (const QString &m : uncMachines)
1497  {
1498  const bool ping = canPing(m);
1499  if (!ping)
1500  {
1501  msgs.push_back(CStatusMessage(this).validationError(u"Cannot ping UNC machine(s): %1. UNC files: %2")
1502  << m << uncFiles.size());
1503  }
1504  }
1505  return msgs;
1506  }
1507 
1508  QJsonObject CAircraftModelList::toMemoizedJson() const
1509  {
1511  QJsonArray array;
1512  for (auto it = cbegin(); it != cend(); ++it) { array << it->toMemoizedJson(helper); }
1513  QJsonObject json;
1514  json.insert("containerbase", array);
1515  json.insert("aircraftIcaos", helper.getTable<CAircraftIcaoCode>().toJson());
1516  json.insert("liveries", helper.getTable<CLivery>().toJson());
1517  json.insert("distributors", helper.getTable<CDistributor>().toJson());
1518  return json;
1519  }
1520 
1521  void CAircraftModelList::convertFromMemoizedJson(const QJsonObject &json, bool fallbackToConvertToJson)
1522  {
1523  clear();
1524  QJsonValue value = json.value("containerbase");
1525  if (value.isUndefined()) { throw CJsonException("Missing 'containerbase'"); }
1526  QJsonArray array = value.toArray();
1527 
1529  const QJsonValue aircraftIcaos = json.value("aircraftIcaos");
1530  const QJsonValue liveries = json.value("liveries");
1531  const QJsonValue distributors = json.value("distributors");
1532 
1533  const bool undefAc = aircraftIcaos.isUndefined();
1534  const bool undefLiv = liveries.isUndefined();
1535  const bool undefDist = distributors.isUndefined();
1536  const bool undefAll = undefAc && undefDist && undefLiv;
1537 
1538  if (fallbackToConvertToJson && undefAll)
1539  {
1540  this->convertFromJson(json);
1541  return;
1542  }
1543  else
1544  {
1545  if (undefAc) { throw CJsonException("Missing 'aircraftIcaos'"); }
1546  if (undefLiv) { throw CJsonException("Missing 'liveries'"); }
1547  if (undefDist) { throw CJsonException("Missing 'distributors'"); }
1548  }
1549 
1550  // convert
1551  {
1552  CJsonScope scope("aircraftIcaos");
1553  Q_UNUSED(scope)
1554  helper.getTable<CAircraftIcaoCode>().convertFromJson(aircraftIcaos.toObject());
1555  }
1556  {
1557  CJsonScope scope("liveries");
1558  Q_UNUSED(scope)
1559  helper.getTable<CLivery>().convertFromJson(liveries.toObject());
1560  }
1561  {
1562  CJsonScope scope("distributors");
1563  Q_UNUSED(scope)
1564  helper.getTable<CDistributor>().convertFromJson(distributors.toObject());
1565  }
1566 
1567  int index = 0;
1568  for (auto i = array.begin(); i != array.end(); ++i)
1569  {
1570  CJsonScope scope("containerbase", index++);
1571  Q_UNUSED(scope)
1572  CAircraftModel value;
1573  value.convertFromMemoizedJson(i->toObject(), helper);
1574  this->push_back(value);
1575  }
1576  }
1577 
1578  QJsonArray CAircraftModelList::toDatabaseJson() const
1579  {
1580  QJsonArray array;
1581  for (const CAircraftModel &model : *this)
1582  {
1583  CAircraftModel copy(model);
1584  copy.normalizeFileNameForDb(); // strip full path
1585  QJsonValue v(copy.toDatabaseJson());
1586  array.append(v);
1587  }
1588  return array;
1589  }
1590 
1591  QString CAircraftModelList::toDatabaseJsonString(QJsonDocument::JsonFormat format) const
1592  {
1593  return QJsonDocument(toDatabaseJson()).toJson(format);
1594  }
1595 
1596  QString CAircraftModelList::asHtmlSummary() const
1597  {
1598  if (this->isEmpty()) { return {}; }
1599  QString html;
1600  for (const CAircraftModel &model : *this)
1601  {
1602  html += html.isEmpty() ? model.asHtmlSummary(" ") : u"<br>" % model.asHtmlSummary(" ");
1603  }
1604  return html;
1605  }
1606 
1607  QString CAircraftModelList::coverageSummary(const QString &separator) const
1608  {
1609  if (this->isEmpty()) { return "no models"; } // avoid division by 0
1610 
1611  const int dbEntries = this->countWithValidDbKey();
1612  const double dbRatio = CMathUtils::round(static_cast<double>(100 * dbEntries) / this->size(), 1);
1613  return u"Entries: " % QString::number(this->size()) % u" | valid DB keys: " % QString::number(dbEntries) %
1614  u" (" % QString::number(dbRatio) % u"%)" % separator % u"color liveries: " %
1615  QString::number(this->countModelsWithColorLivery()) % u" | airline liveries: " %
1616  QString::number(this->countModelsWithAirlineLivery()) % separator % u"VTOL: " %
1617  QString::number(this->countVtolAircraft()) % u" | military: " %
1618  QString::number(this->countMilitaryAircraft()) % u" | civilian: " %
1619  QString::number(this->countCivilianAircraft()) % separator % u"Different airlines: " %
1620  QString::number(this->countDifferentAirlines()) % separator % u"Combined types: '" %
1621  this->getCombinedTypesAsString() % u'\'' % separator %
1622  (this->size() <= 25 ?
1623  (u"Aircraft/airlines: " % this->getAicraftAndAirlineDesignatorsAsString(true) % separator) :
1624  QString()) %
1625  u"Simulators: " % this->countPerSimulator().toQString();
1626  }
1627 
1628  QString CAircraftModelList::coverageSummaryForModel(const CAircraftModel &checkModel,
1629  const QString &separator) const
1630  {
1631  const bool combinedCodeForModel =
1632  this->containsCombinedType(checkModel.getAircraftIcaoCode().getCombinedType());
1633  const bool airlineForModel =
1634  checkModel.hasAirlineDesignator() && this->containsAirlineLivery(checkModel.getAirlineIcaoCode());
1635  return coverageSummary(separator) % separator % u"Data for input model, has combined: " %
1636  boolToYesNo(combinedCodeForModel) %
1637  (checkModel.hasAirlineDesignator() ?
1638  u" airline '" % checkModel.getAirlineIcaoCodeDesignator() % u"': " % boolToYesNo(airlineForModel) :
1639  QString());
1640  }
1641 
1642  QString CAircraftModelList::htmlStatistics(bool aircraftStats, bool airlineStats) const
1643  {
1644  static const QString sep("<br>");
1645  const bool notOnlyDb = this->containsAnyObjectWithoutKey();
1646  QString stats = this->coverageSummary(sep);
1647  if (aircraftStats)
1648  {
1649  const CAircraftIcaoCodeList icaos = this->getAircraftIcaoCodesFromDb();
1650  QStringList designators(icaos.allDesignators().values());
1651  designators.sort();
1652  stats += sep % sep % u"Aircraft ICAOs from DB: " % sep % designators.join(", ");
1653  }
1654 
1655  if (airlineStats)
1656  {
1657  const CAirlineIcaoCodeList icaos = this->getAirlineIcaoCodesFromDb();
1658  const QStringList designators = icaos.toIcaoDesignatorCompleterStrings();
1659  stats += sep % sep % u"Airline ICAOs from DB: " % sep % designators.join(", ");
1660  }
1661 
1662  if (notOnlyDb)
1663  {
1664  const CAircraftModelList dbModels = this->findObjectsWithDbKey();
1665  stats += sep % sep % u"DB objects:<br>---------" % sep % dbModels.htmlStatistics(false, false);
1666  }
1667  return stats;
1668  }
1669 
1670  CStatusMessage CAircraftModelList::saveInvalidModels() const
1671  {
1672  if (this->isEmpty()) { return CStatusMessage(this).info(u"No models"); }
1673  const QString json = this->toJsonString();
1674  const bool s = CFileUtils::writeStringToFile(json, invalidModelFileAndPath());
1675  if (!s)
1676  {
1677  return CStatusMessage(this).error(u"Unable to save %1 entries to '%2'")
1678  << this->size() << invalidModelFileAndPath();
1679  }
1680  return CStatusMessage(this).info(u"Saved %1 entries to '%2'") << this->size() << invalidModelFileAndPath();
1681  }
1682 
1683  CStatusMessage CAircraftModelList::loadInvalidModels()
1684  {
1685  const QString json = CFileUtils::readFileToString(invalidModelFileAndPath());
1686  if (json.isEmpty())
1687  {
1688  return CStatusMessage(this).error(u"Unable to read from '%1'") << invalidModelFileAndPath();
1689  }
1690  *this = CAircraftModelList::fromJson(json, true);
1691  return CStatusMessage(this).info(u"Loaded %1 entries from '%2'") << this->size() << invalidModelFileAndPath();
1692  }
1693 
1694  CAircraftModelList CAircraftModelList::fromDatabaseJsonCaching(const QJsonArray &array,
1695  const CAircraftIcaoCodeList &icaos,
1696  const CAircraftCategoryList &categories,
1697  const CLiveryList &liveries,
1698  const CDistributorList &distributors)
1699  {
1700  AircraftIcaoIdMap aircraftIcaosMap = icaos.toDbKeyValueMap();
1701  LiveryIdMap liveriesMap = liveries.toDbKeyValueMap();
1702  DistributorIdMap distributorsMap = distributors.toDbKeyValueMap();
1703  const AircraftCategoryIdMap categoriesMap = categories.toDbKeyValueMap();
1704 
1705  CAircraftModelList models;
1706  for (const QJsonValue &value : array)
1707  {
1708  models.push_back(CAircraftModel::fromDatabaseJsonCaching(value.toObject(), aircraftIcaosMap, categoriesMap,
1709  liveriesMap, distributorsMap));
1710  }
1711  return models;
1712  }
1713 
1714  const QString &CAircraftModelList::invalidModelFileAndPath()
1715  {
1716  static const QString f =
1717  CFileUtils::appendFilePathsAndFixUnc(CSwiftDirectories::logDirectory(), "invalidmodels.json");
1718  return f;
1719  }
1720 
1721  bool CAircraftModelList::hasInvalidModelFile()
1722  {
1723  const QFileInfo fi(invalidModelFileAndPath());
1724  return fi.exists();
1725  }
1726 
1727  void CAircraftModelList::addAsValidOrInvalidModel(const CAircraftModel &model, bool valid,
1728  CAircraftModelList &validModels,
1729  CAircraftModelList &invalidModels)
1730  {
1731  if (valid)
1732  {
1733  validModels.push_back(model);
1734  invalidModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive);
1735  }
1736  else
1737  {
1738  invalidModels.push_back(model);
1739  validModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive);
1740  }
1741  }
1742 
1743  void CAircraftModelList::addAsValidOrInvalidModels(const CAircraftModelList &models, bool valid,
1744  CAircraftModelList &validModels,
1745  CAircraftModelList &invalidModels)
1746  {
1747  for (const CAircraftModel &model : models)
1748  {
1749  CAircraftModelList::addAsValidOrInvalidModel(model, valid, validModels, invalidModels);
1750  }
1751  }
1752 } // namespace swift::misc::simulation
QMultiMap< int, CAircraftModel > ScoredModels
Individual (matching) score for each model.
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
Pseudo-RAII pattern that tracks the current JSON value being converted.
Definition: jsonexception.h:50
Memoizer for Ts. Other types are passed through.
Definition: memotable.h:49
const CSequence< T > & getTable() const
Return the values in the T table as a flat list.
Definition: memotable.h:61
Unmemoizer for Ts. Other types are passed through.
Definition: memotable.h:69
CSequence< T > & getTable()
Return reference to the flat list T table.
Definition: memotable.h:73
Derived & validationError(const char16_t(&format)[N])
Set the severity to error, providing a format string, and adding the validation category.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
auto findFirstByOrDefault(Predicate p, const Value &def) const
Return a copy of the first element for which a given predicate returns true, or a default value if th...
Definition: range.h:70
bool containsBy(Predicate p) const
Return true if there is an element for which a given predicate returns true.
Definition: range.h:101
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
CSequence findBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: sequence.h:398
void truncate(size_type maxSize)
Changes the size of the sequence, if it is bigger than the given size.
Definition: sequence.h:291
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
void sort(Predicate p)
In-place sort by a given comparator predicate.
Definition: sequence.h:560
Build a QSet more efficiently when calling insert() in a for loop.
Definition: setbuilder.h:25
void insert(const T &value)
Add an element to the set. Runs in amortized constant time.
Definition: setbuilder.h:29
Streamable status message, e.g.
bool isWarningOrAbove() const
Warning or above.
void prependMessage(const QString &msg)
Prepend message.
Status messages, e.g. from Core -> GUI.
CStatusMessage toSingleMessage() const
Merge into a single message.
bool hasErrorMessages() const
Error messages.
Value object for aircraft categories.
Value object encapsulating a list of ICAO codes.
Value object for ICAO classification.
CWakeTurbulenceCategory getWtc() const
Get WTC.
const QString & getFamily() const
Family (e.g. A350)
bool matchesDesignator(const QString &designator, int fuzzyMatch=-1, int *result=nullptr) const
Matches designator string?
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
QString getDesignatorDbKey() const
Designator and DB key.
bool matchesCombinedType(const QString &combinedType) const
Matches given combined code.
const QString & getCombinedType() const
Get type, e.g. "L2J".
const CAircraftCategory & getCategory() const
Get category.
const QString & getManufacturer() const
Get manufacturer, e.g. "Airbus".
Value object encapsulating a list of ICAO codes.
QSet< QString > allDesignators(bool noUnspecified=true) const
All ICAO codes, no duplicates.
Value object for ICAO classification.
QString getDesignatorDbKey() const
Designator and DB key.
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasValidDesignator() const
Airline designator available?
bool matchesNamesOrTelephonyDesignator(const QString &candidate) const
Relaxed check by name or telephony designator (aka callsign, not to be confused with CCallsign)
Value object encapsulating a list of ICAO codes.
QStringList toIcaoDesignatorCompleterStrings(bool combinedString=true, bool sort=true) const
String list for completion by ICAO designator.
QSet< QString > allDesignators() const
All designators.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Value object encapsulating information about an airpot.
Definition: livery.h:29
double getColorDistance(const CLivery &otherLivery) const
Color distance 0..1 (0 is best)
Definition: livery.cpp:182
bool matchesCombinedCode(const QString &candidate) const
Matches combined code.
Definition: livery.cpp:101
const CAirlineIcaoCode & getAirlineIcaoCode() const
Corresponding airline, if any.
Definition: livery.h:65
bool hasColorTail() const
Tail color set?
Definition: livery.cpp:97
bool hasColorFuselage() const
Fuselage color set?
Definition: livery.cpp:95
const QString & getCombinedCode() const
Combined code.
Definition: livery.h:71
bool isAirlineLivery() const
Livery representing airline.
Definition: livery.cpp:167
bool hasCombinedCode() const
Livery combined code available?
Definition: livery.cpp:161
bool isColorLivery() const
Color livery?
Definition: livery.cpp:180
Value object for a list of airports.
Definition: liverylist.h:29
QMap< KEYTYPE, OBJ > toDbKeyValueMap() const
As map with DB key/object.
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:195
const QString & getDbKey() const
Get DB key.
Definition: datastore.h:180
QJsonObject toJson() const
Cast to JSON object.
Definition: mixinjson.h:132
Physical unit length (length)
Definition: length.h:18
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
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 setDistributorOrder(int order)
Set the distributor order.
bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string?
ModelMode getModelMode() const
Model mode.
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code.
bool hasKnownAircraftDesignator() const
Has known aircraft designator?
bool hasCategory() const
Assigned a category?
bool hasAirlineDesignator() const
Airline designator?
bool hasExistingCorrespondingFile() const
Does the corresponding file exist?
const aviation::CLivery & getLivery() const
Get livery.
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.
const QString & getAircraftIcaoCodeDesignator() const
Aircraft ICAO code designator.
CSimulatorInfo getSimulator() const
Simulator info.
void normalizeFileNameForDb()
File path for DB (absolute paths make no sense in DB)
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
bool hasDistributor() const
Distributor, but not necessarily loaded from DB.
bool isCivilian() const
Civilian model?
const QString & getModelModeAsString() const
Model mode as string.
bool hasAircraftDesignator() const
Has aircraft designator?
QJsonObject toDatabaseJson() const
To database JSON.
bool matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string or alias?
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.
bool matchesFileName(const QString &fileName) const
Matching file name?
bool hasDbDistributor() const
Distributor loaded from DB.
QString getModelStringAndDbKey() const
Model string and DB key (if available)
const QString getAirlineIcaoCodeVDesignator() const
Airline ICAO code designator.
QString getFileNameLowerCase() const
File name as lower case.
bool matchesAnyDbDistributor(const CDistributorList &distributors) const
By distributor.
QString asHtmlSummary(const QString &separator="<br>") const
As a brief HTML summary (e.g. used in tooltips)
const QString & getAirlineIcaoCodeDesignator() const
Airline ICAO code designator.
bool isMilitary() const
Military model?
bool hasModelString() const
Non empty model string?
void setLivery(const aviation::CLivery &livery)
Livery.
void setDistributor(const CDistributor &distributor)
Set distributor.
bool setAircraftIcaoCode(const aviation::CAircraftIcaoCode &aircraftIcaoCode)
Set aircraft ICAO code.
const QString & getFileName() const
File name (corresponding data for simulator, only available if representing simulator model.
bool matchesSimulator(const CSimulatorInfo &simulator) const
Matches given simulator?
Value object encapsulating a list of aircraft models.
bool containsModelStringOrDbKey(const simulation::CAircraftModel &model, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model with model string or id?
bool containsAirlineLivery(const aviation::CAirlineIcaoCode &airline) const
Contains airline livery for given airline.
QStringList getModelStringList(bool sort=true) const
Model strings.
CAircraftModel findFirstByModelStringAliasOrDefault(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find first by model string.
QSet< QString > getAllUNCFileNames() const
All UNC file names.
CAircraftModelList findEmptyModelStrings() const
Find empty model strings.
CAircraftModel findFirstByModelStringOrDefault(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find first by model string.
CAircraftModelList findByAircraftAndAirline(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CAirlineIcaoCode &airlineIcaoCode) const
Find by ICAO of aircraft and airline.
CAircraftModelList findByAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) const
Find by designator and livery code.
bool containsCallsign(const aviation::CCallsign &callsign) const
Contains model for callsign?
CAircraftModelList findByAirlineGroup(const swift::misc::aviation::CAirlineIcaoCode &airline) const
Find by the corresponding airline group.
CAircraftModelList findColorLiveries() const
Find color liveries.
CAircraftModelList findByIcaoDesignators(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CAirlineIcaoCode &airlineIcaoCode) const
Find by ICAO designators.
CAircraftModelList findByAircraftAndLivery(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CLivery &livery) const
Find by aircraft and livery.
int removeModelsWithString(const CAircraftModelList &models, Qt::CaseSensitivity sensitivity)
Remove those models with given model strings.
QString htmlStatistics(bool aircraftStats, bool airlineStats) const
A HTML summary of the data in the list.
CAircraftModelList findByModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find by model string.
CAircraftModel findFirstByCallsignOrDefault(const aviation::CCallsign &callsign) const
Find first by callsign.
bool removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity)
Remove those models with given model strings.
CAircraftModelList findByAirlineNameAndTelephonyDesignator(const QString &name, const QString &telephony, bool onlyIfExistInModel=true) const
Find by airline name and telephony.
bool containsCombinedType(const QString &combinedType) const
Contains given combined type?
bool containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model string?
bool containsModelsWithAircraftAndAirlineIcaoDesignator(const QString &aircraftDesignator, const QString &airlineDesignator) const
Contains any model with aircraft and airline ICAO designator?
bool containsModelsWithAircraftIcaoDesignator(const QString &aircraftDesignator) const
Contains any model with aircraft ICAO designator?
Count per simulator, small utility class allows to retrieve values as per simulator.
void increaseSimulatorCounts(const CSimulatorInfo &simulator)
Increase all simulators given here.
int simulatorsRepresented() const
Number of simulators with count > 0.
QMultiMap< int, CSimulatorInfo > countPerSimulator() const
Sorted (ascending) per simulator.
Value object encapsulating information of software distributor.
Definition: distributor.h:33
Value object encapsulating a list of distributors.
int removeIfNotLoadedFromDb()
Remove distributors not from DB.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
void addSimulator(Simulator s)
Add simulator flags.
bool matchesAny(const CSimulatorInfo &otherInfo) const
Matches any simulator.
Simulator getSimulator() const
Simulator.
auto removeIfIn(I begin1, I end1, J begin2, J end2)
Removes those elements in range 1 that appear also in range 2 leaving only those that do not appear i...
Definition: algorithm.h:25
SWIFT_MISC_EXPORT QString joinStringSet(const QSet< QString > &set, const QString &separator)
Convert string to bool.
auto makePairsRange(const T &container)
Returns a const CRange for iterating over the keys and values of a Qt associative container.
Definition: range.h:374
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition: sequence.h:63