swift
categorymatcher.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QStringBuilder>
7 
11 #include "misc/statusmessagelist.h"
12 
13 using namespace swift::misc::aviation;
14 
15 namespace swift::misc::simulation
16 {
17  const QStringList &CCategoryMatcher::getLogCategories()
18  {
19  static const QStringList cats { CLogCategories::matching() };
20  return cats;
21  }
22 
23  void CCategoryMatcher::setCategories(const CAircraftCategoryList &categories)
24  {
25  m_all = categories;
26  m_all.sortByLevel();
27 
28  CAircraftCategoryList gliders = categories.findByName("glider").findFirstLevels();
29  if (!gliders.isEmpty())
30  {
31  const int fl = gliders.front().getFirstLevel();
32  gliders = categories.findByFirstLevel(fl);
33  gliders.sortByLevel();
34  m_gliders = gliders;
35  }
36 
37  CAircraftCategoryList militaryWing = categories.findByName("wing military").findFirstLevels();
38  if (!militaryWing.isEmpty())
39  {
40  const int fl = militaryWing.front().getFirstLevel();
41  militaryWing = categories.findByFirstLevel(fl);
42  militaryWing.sortByLevel();
43  m_militaryWingAircraft = militaryWing;
44  }
45 
46  CAircraftCategoryList militaryRotor = categories.findByName("rotor military").findFirstLevels();
47  if (!militaryRotor.isEmpty())
48  {
49  const int fl = militaryRotor.front().getFirstLevel();
50  militaryRotor = categories.findByFirstLevel(fl);
51  militaryRotor.sortByLevel();
52  m_militaryRotorAircraft = militaryRotor;
53  }
54  }
55 
56  CAircraftModelList CCategoryMatcher::reduceByCategories(const CAircraftModelList &alreadyMatchedModels,
57  const CAircraftModelList &modelSet,
58  const CAircraftMatcherSetup &setup,
59  const CSimulatedAircraft &remoteAircraft, bool &reduced,
60  bool shortLog, CStatusMessageList *log) const
61  {
62  Q_UNUSED(shortLog)
63 
64  reduced = false;
65  if (!setup.useCategoryMatching())
66  {
67  if (log)
68  {
69  CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QStringLiteral("Disabled category matching"),
70  getLogCategories());
71  }
72  return alreadyMatchedModels;
73  }
74  if (m_all.isEmpty())
75  {
76  // no categories?
77  CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QStringLiteral("Disabled category matching"),
78  getLogCategories());
79  return alreadyMatchedModels;
80  }
81  if (!remoteAircraft.getAircraftIcaoCode().hasCategory())
82  {
83  CMatchingUtils::addLogDetailsToList(log, remoteAircraft, QStringLiteral("No category in remote aircraft"),
84  getLogCategories());
85  return alreadyMatchedModels;
86  }
87 
88  if (log)
89  {
90  CMatchingUtils::addLogDetailsToList(
91  log, remoteAircraft,
92  QStringLiteral("Remote aircraft has category '%1'")
93  .arg(remoteAircraft.getAircraftIcaoCode().getCategory().getNameDbKey()),
94  getLogCategories());
95  }
96  if (!m_gliders.isEmpty() && setup.getMatchingMode().testFlag(CAircraftMatcherSetup::ByCategoryGlider) &&
97  this->isGlider(remoteAircraft.getAircraftIcaoCode()))
98  {
99  // we have a glider
100  // and we search in the whole set: this is a special case
101  const int firstLevel = this->gliderFirstLevel();
102  const CAircraftModelList gliders =
103  modelSet.findByCategoryFirstLevel(firstLevel); // all gliders from model set
104  if (!gliders.isEmpty())
105  {
106  const CAircraftCategory category = remoteAircraft.getAircraftIcaoCode().getCategory();
107  reduced = true; // in any case reduced (to gliders)
108 
109  // find same category
110  const CAircraftModelList sameGliders = gliders.findByCategory(category);
111  if (!sameGliders.isEmpty())
112  {
113  if (log)
114  {
115  CMatchingUtils::addLogDetailsToList(log, remoteAircraft,
116  QStringLiteral("Reduced to %1 models by category: '%2'")
117  .arg(sameGliders.size())
118  .arg(category.toQString(true)),
119  getLogCategories());
120  }
121  return sameGliders;
122  }
123 
124  // find parallel branch category
125  const CAircraftCategoryList otherBranches = m_gliders.findInParallelBranch(category);
126  if (!otherBranches.isEmpty())
127  {
128  const CAircraftModelList otherBranchGliders = gliders.findByCategories(otherBranches);
129  if (!otherBranchGliders.isEmpty())
130  {
131  if (log)
132  {
133  CMatchingUtils::addLogDetailsToList(
134  log, remoteAircraft,
135  QStringLiteral("Reduced to %1 parallel branch models of '%2' by categories: '%3'")
136  .arg(otherBranchGliders.size())
137  .arg(category.getLevelAndName(), otherBranches.getLevelsString()),
138  getLogCategories());
139  }
140  return otherBranchGliders;
141  }
142  }
143 
144  const CAircraftCategoryList siblings = m_gliders.findSiblings(category);
145  if (!siblings.isEmpty())
146  {
147  const CAircraftModelList siblingGliders = modelSet.findByCategories(siblings);
148  if (!siblings.isEmpty() && !siblingGliders.isEmpty())
149  {
150  if (log)
151  {
152  CMatchingUtils::addLogDetailsToList(
153  log, remoteAircraft,
154  QStringLiteral("Reduced to %1 sibling models of '%2' by categories: '%3'")
155  .arg(siblingGliders.size())
156  .arg(category.getLevelAndName(), siblings.getLevelsString()),
157  getLogCategories());
158  }
159  return siblingGliders;
160  }
161  }
162 
163  CMatchingUtils::addLogDetailsToList(
164  log, remoteAircraft,
165  QStringLiteral("Reduced to %1 models by 'GLIDER' category").arg(sameGliders.size()),
166  getLogCategories());
167  return gliders;
168  }
169  else
170  {
171  if (log)
172  {
173  CMatchingUtils::addLogDetailsToList(
174  log, remoteAircraft,
175  QStringLiteral("No glider category '%1' in set").arg(m_gliders.front().getLevelAndName()),
176  getLogCategories());
177  }
178  static const QStringList substituteIcaos({ "UHEL", "GLID", "ULAC" }); // maybe also GYRO
179  static const QString substituteIcaosStr = substituteIcaos.join(", ");
180 
181  CAircraftModelList substitutes =
182  alreadyMatchedModels.findByDesignatorsOrFamilyWithColorLivery(substituteIcaos);
183  if (substitutes.isEmpty())
184  {
185  substitutes = alreadyMatchedModels.findByCombinedType(QStringLiteral("L1P"));
186  if (!substitutes.isEmpty())
187  {
188  reduced = true;
189  if (log)
190  {
191  CMatchingUtils::addLogDetailsToList(
192  log, remoteAircraft,
193  QStringLiteral("No gliders, reduced to 'L1P' models: %1' (avoid absurd matchings)")
194  .arg(substitutes.size()),
195  getLogCategories());
196  }
197  return substitutes;
198  }
199  }
200  else
201  {
202  reduced = true;
203  if (log)
204  {
205  CMatchingUtils::addLogDetailsToList(log, remoteAircraft,
206  QStringLiteral("Reduced to %1 models by '%2'")
207  .arg(substitutes.size())
208  .arg(substituteIcaosStr),
209  getLogCategories());
210  }
211  return substitutes;
212  }
213  }
214  }
215 
216  return alreadyMatchedModels;
217  }
218 
219  bool CCategoryMatcher::isGlider(const CAircraftIcaoCode &icao) const
220  {
221  if (icao.getDesignator() == CAircraftIcaoCode::getGliderDesignator()) { return true; }
222  const int glider1st = this->gliderFirstLevel();
223  if (glider1st >= 0 && icao.hasCategory()) { return icao.getCategory().getFirstLevel() == glider1st; }
224  return false;
225  }
226 
227  int CCategoryMatcher::gliderFirstLevel() const
228  {
229  if (m_gliders.isEmpty()) { return -1; }
230  return m_gliders.front().getFirstLevel();
231  }
232 } // namespace swift::misc::simulation
static const QString & matching()
Matching.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
reference front()
Access the first element.
Definition: sequence.h:225
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Status messages, e.g. from Core -> GUI.
Value object for aircraft categories.
QString getLevelAndName() const
Level and name.
QString getNameDbKey() const
Designator and DB key.
Value object encapsulating a list of ICAO codes.
QString getLevelsString(const QString &separator=", ") const
Get all level strings.
CAircraftCategoryList findInParallelBranch(const CAircraftCategory &category) const
Find siblings.
CAircraftCategoryList findByFirstLevel(int level) const
Find by first level.
CAircraftCategoryList findFirstLevels() const
Find first levels.
CAircraftCategoryList findByName(const QString &name, Qt::CaseSensitivity cs=Qt::CaseInsensitive) const
Find by name.
CAircraftCategoryList findSiblings(const CAircraftCategory &category) const
Find siblings.
Value object for ICAO classification.
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
const CAircraftCategory & getCategory() const
Get category.
static const QString & getGliderDesignator()
Get the glider designator.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
MatchingMode getMatchingMode() const
Matching mode.
bool useCategoryMatching() const
Use category matching.
Value object encapsulating a list of aircraft models.
CAircraftModelList findByCategories(const aviation::CAircraftCategoryList &categories) const
Find by categories.
CAircraftModelList findByCategoryFirstLevel(int firstLevel) const
Find by first level of category.
CAircraftModelList findByCombinedType(const QString &combinedType) const
Find by combined code, wildcards possible, e.g. L*P, *2J.
CAircraftModelList findByCategory(const aviation::CAircraftCategory &category) const
Find by category.
CAircraftModelList findByDesignatorsOrFamilyWithColorLivery(const QStringList &designators) const
Models with aircraft family or designators and color livery.
Comprehensive information of an aircraft.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.