swift
modelmatchercomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QCheckBox>
7 #include <QCompleter>
8 #include <QLineEdit>
9 #include <QPushButton>
10 #include <QStringBuilder>
11 #include <QStringList>
12 #include <QTabWidget>
13 #include <QTextEdit>
14 #include <QtGlobal>
15 
16 #include "ui_modelmatchercomponent.h"
17 
18 #include "core/webdataservices.h"
25 #include "gui/guiapplication.h"
26 #include "gui/guiutility.h"
28 #include "gui/uppercasevalidator.h"
33 #include "misc/aviation/callsign.h"
34 #include "misc/aviation/livery.h"
35 #include "misc/network/user.h"
38 #include "misc/statusmessagelist.h"
39 
40 using namespace swift::misc;
41 using namespace swift::misc::aviation;
42 using namespace swift::misc::simulation;
43 using namespace swift::misc::simulation::data;
44 using namespace swift::misc::network;
45 using namespace swift::gui::models;
46 using namespace swift::gui::views;
47 using namespace swift::core;
48 
49 namespace swift::gui::components
50 {
51  CModelMatcherComponent::CModelMatcherComponent(QWidget *parent) : QFrame(parent), ui(new Ui::CModelMatcherComponent)
52  {
53  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Missing sGui");
54  Q_ASSERT_X(sGui->getWebDataServices(), Q_FUNC_INFO, "Missing web services");
55 
56  ui->setupUi(this);
57  ui->comp_SimulatorSelector->setMode(CSimulatorSelector::RadioButtons);
58  ui->comp_SimulatorSelector->setRememberSelectionAndSetToLastSelection();
59  ui->comp_AirlineSelector->displayWithIcaoDescription(false);
60  ui->comp_AircraftSelector->displayWithIcaoDescription(false);
61  ui->comp_LiverySelector->withLiveryDescription(false);
62  ui->tvp_ResultMessages->setMode(CStatusMessageListModel::Simplified);
63  ui->tvp_ResultMessages->menuAddItems(CViewBaseNonTemplate::MenuSave);
64 
65  const CUpperCaseValidator *validator = new CUpperCaseValidator(this);
66  ui->le_ModelString->setValidator(validator);
67  ui->le_Manufacturer->setValidator(validator);
68  ui->le_Callsign->setValidator(validator);
69 
70  connect(ui->comp_SimulatorSelector, &CSimulatorSelector::changed, this,
71  &CModelMatcherComponent::onSimulatorChanged);
72  connect(sGui->getWebDataServices(), &CWebDataServices::dataRead, this, &CModelMatcherComponent::onWebDataRead,
73  Qt::QueuedConnection);
74 
75  connect(ui->pb_ModelMatching, &QPushButton::pressed, this, &CModelMatcherComponent::testModelMatching);
76  connect(ui->pb_ReverseLookup, &QPushButton::pressed, this, &CModelMatcherComponent::reverseLookup);
77  connect(ui->pb_Settings, &QPushButton::pressed, this, &CModelMatcherComponent::displaySettingsDialog);
78 
79  connect(ui->cb_UseWorkbench, &QCheckBox::toggled, this, &CModelMatcherComponent::onWorkbenchToggled);
80 
81  // initial settings
82  m_matcher.setSetup(m_matchingSettings.get());
83 
84  this->redisplay();
85  ui->cb_UseWorkbench->setVisible(false);
86  }
87 
89 
91  {
92  if (index < 0) { return; }
93  const QTabWidget *tw = CGuiUtility::parentTabWidget(this);
94  Q_ASSERT_X(tw, Q_FUNC_INFO, "Cannot find parent tab widget");
95  const QWidget *tabWidget = tw->currentWidget();
96  const QWidget *myselfTabWidget = this->parentWidget();
97  if (tabWidget != myselfTabWidget) { return; }
98  this->redisplay();
99  }
100 
102  {
103  if (workbenchView)
104  {
105  ui->cb_UseWorkbench->setVisible(true);
106  m_workbenchView = workbenchView;
107  }
108  else
109  {
110  ui->cb_UseWorkbench->setVisible(false);
111  m_workbenchView.clear();
112  }
113  }
114 
115  void CModelMatcherComponent::onSimulatorChanged(const CSimulatorInfo &simulator)
116  {
117  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
118 
119  ui->tvp_ResultMessages->clear();
120  if (this->useWorkbench())
121  {
122  const CAircraftModelList models = m_workbenchView->container();
123  if (models.isEmpty())
124  {
125  CStatusMessage m(this, CStatusMessage::SeverityWarning, u"No models in workbench, disabled.");
126  ui->tvp_ResultMessages->insert(m);
127  return;
128  }
130  m_matcher.setModelSet(models, si, true);
131  }
132  else
133  {
134  const CAircraftModelList models =
135  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
136  m_matcher.setModelSet(models, simulator, true);
137  }
138  this->redisplay();
139  }
140 
141  void CModelMatcherComponent::onWorkbenchToggled(bool checked)
142  {
143  Q_UNUSED(checked);
144  this->onSimulatorChanged(ui->comp_SimulatorSelector->getValue());
145  }
146 
147  void CModelMatcherComponent::onCacheChanged(const CSimulatorInfo &simulator)
148  {
149  Q_UNUSED(simulator);
150  this->redisplay();
151  }
152 
153  void CModelMatcherComponent::testModelMatching()
154  {
155  ui->te_Results->clear();
156  this->onSimulatorChanged(ui->comp_SimulatorSelector->getValue()); // update model set to latest version
157  CStatusMessageList msgs;
158  CSimulatedAircraft remoteAircraft(this->createAircraft());
159  m_matcher.setDefaultModel(CModelMatcherComponent::defaultModel());
160 
161  if (ui->cb_withReverseLookup->isChecked())
162  {
163  const QString liveryString(ui->comp_LiverySelector->getRawCombinedCode());
164  const CAircraftModelList modelSet = m_matcher.getModelSet();
165  const CAircraftMatcherSetup setup = m_matcher.getSetup();
166  const CAircraftModel reverseModel =
167  CAircraftMatcher::reverseLookupModelMs(remoteAircraft.getModel(), liveryString, setup, modelSet, &msgs);
168  remoteAircraft.setModel(reverseModel); // current model
169  }
170 
171  CStatusMessageList matchingMsgs;
172  const CAircraftModel matched =
173  m_matcher.getClosestMatch(remoteAircraft, MatchingLogAll, &matchingMsgs, true); // test model matching
174  msgs.push_back(matchingMsgs);
175 
176  ui->te_Results->setText(matched.toQString(true));
177  ui->tvp_ResultMessages->updateContainer(msgs);
178  ui->tvp_ResultMessages->fullResizeToContents();
179  ui->tvp_ResultMessages->resizeRowsToContents();
180  }
181 
182  void CModelMatcherComponent::reverseLookup()
183  {
184  // CAirspaceMonitor::reverseLookupModelWithFlightplanData is the real world lookup
185 
186  ui->te_Results->clear();
187  CStatusMessageList msgs;
188  m_matcher.setDefaultModel(CModelMatcherComponent::defaultModel());
189 
190  const CAircraftModelList modelSet = m_matcher.getModelSet();
191  const CAircraftMatcherSetup setup = m_matcher.getSetup();
192  const CSimulatedAircraft remoteAircraft(createAircraft());
193  const QString livery(ui->comp_LiverySelector->getRawCombinedCode());
194  const CAircraftModel matched =
195  CAircraftMatcher::reverseLookupModelMs(remoteAircraft.getModel(), livery, setup, modelSet, &msgs);
196  ui->te_Results->setText(matched.toQString(true));
197  ui->tvp_ResultMessages->updateContainer(msgs);
198  }
199 
200  void CModelMatcherComponent::onWebDataRead(CEntityFlags::Entity entity, CEntityFlags::ReadState state, int number,
201  const QUrl &url)
202  {
203  Q_UNUSED(url)
204 
205  if (!sGui || sGui->isShuttingDown()) { return; }
206  if (number > 0 && entity.testFlag(CEntityFlags::ModelEntity) && CEntityFlags::isFinishedReadState(state))
207  {
208  const QStringList modelStrings(sGui->getWebDataServices()->getModelStrings(true));
209  ui->le_ModelString->setCompleter(new QCompleter(modelStrings, this));
210  }
211  }
212 
213  void CModelMatcherComponent::displaySettingsDialog()
214  {
215  if (!m_settingsDialog) { m_settingsDialog = new CSettingsMatchingDialog(this); }
216  m_settingsDialog->setMatchingSetup(m_matcher.getSetup());
217  const QDialog::DialogCode r = static_cast<QDialog::DialogCode>(m_settingsDialog->exec());
218  if (r == QDialog::Accepted) { m_matcher.setSetup(m_settingsDialog->getMatchingSetup()); }
219  }
220 
221  void CModelMatcherComponent::redisplay()
222  {
223  const int c = this->getMatcherModelsCount();
224  ui->le_ModelSetCount->setText(QString::number(c) % (this->useWorkbench() ? u" (workbench)" : u""));
225  }
226 
227  CAircraftModelList CModelMatcherComponent::getModelSetModels() const
228  {
229  const CSimulatorInfo simulator = ui->comp_SimulatorSelector->getValue();
230  const CAircraftModelList models =
231  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
232  return models;
233  }
234 
235  int CModelMatcherComponent::getMatcherModelsCount() const { return m_matcher.getModelSetCount(); }
236 
237  bool CModelMatcherComponent::useWorkbench() const { return ui->cb_UseWorkbench->isChecked() && m_workbenchView; }
238 
239  CSimulatedAircraft CModelMatcherComponent::createAircraft() const
240  {
241  const QString airline(ui->comp_AirlineSelector->getRawDesignator());
242  const QString aircraft(ui->comp_AircraftSelector->getRawDesignator());
243  const QString modelString(ui->le_ModelString->text().trimmed().toUpper());
244  const QString combined(ui->comp_CombinedCode->getCombinedType());
245  const QString manufacturer(ui->le_Manufacturer->text().trimmed().toUpper());
246  const QString liveryCombinedCode(ui->comp_LiverySelector->getRawCombinedCode());
247  const CCallsign cs(ui->le_Callsign->text().isEmpty() ? "SWIFT" : ui->le_Callsign->text()); // need callsign
248  static const CUser pilot("123456", "swift Test", cs);
249 
250  CAircraftIcaoCode icao(aircraft, combined);
251  icao.setManufacturer(manufacturer);
252 
253  const CAirlineIcaoCode airlineIcao(airline);
254  const CLivery livery(liveryCombinedCode, airlineIcao, "");
255  CAircraftModel m(modelString, CAircraftModel::TypeFSInnData);
256  m.setLivery(livery);
257  m.setCallsign(cs);
258  m.setModelType(modelString.isEmpty() ? CAircraftModel::TypeQueriedFromNetwork : CAircraftModel::TypeFSInnData);
259  CSimulatedAircraft sa(m);
260  sa.setPilot(pilot);
261  sa.setAircraftIcaoCode(icao);
262  return sa;
263  }
264 
265  CAircraftModel CModelMatcherComponent::defaultModel() const
266  {
267  // can somehow dynamilcally determine the models
268  const CAircraftIcaoCode icaoAircraft("B737", "L2J", "FooBar", "Dummy", CWakeTurbulenceCategory::MEDIUM, false,
269  false, false, 1);
270  const CAirlineIcaoCode icaoAirline("Foo", "FooBar airlines", { "DE", "Germany" }, "FOO", true, true);
271  const CLivery livery(CLivery::getStandardCode(icaoAirline), icaoAirline, "Standard Foo airlines", "red", "blue",
272  false);
273  CAircraftModel model("default model", CAircraftModel::TypeOwnSimulatorModel, "dummy model", icaoAircraft,
274  livery);
275  if (model.getCallsign().isEmpty()) { model.setCallsign("SWIFT"); }
276  return model;
277  }
278 
279  MatchingScriptReturnValues CModelMatcherComponent::matchingScript(const CAircraftModel &inModel,
280  const CAircraftMatcherSetup &setup,
281  const CAircraftModelList &modelSet,
282  CStatusMessageList &msgs)
283  {
284  // Script
285  if (setup.doRunMsReverseLookupScript())
286  {
287  const MatchingScriptReturnValues rv =
288  CAircraftMatcher::reverseLookupScript(inModel, setup, modelSet, &msgs);
289  if (rv.runScriptAndModified()) { return rv; }
290  else
291  {
292  CCallsign::addLogDetailsToList(&msgs, inModel.getCallsign(),
293  QStringLiteral("Matching script, no modification"));
294  }
295  }
296  else
297  {
298  CCallsign::addLogDetailsToList(&msgs, inModel.getCallsign(),
299  QStringLiteral("No reverse lookup script used"));
300  }
301 
302  return inModel;
303  }
304 } // namespace swift::gui::components
void setDefaultModel(const swift::misc::simulation::CAircraftModel &defaultModel)
Set default model, can be set by driver specific for simulator.
swift::misc::simulation::CAircraftMatcherSetup getSetup() const
Get the setup.
virtual int getModelSetCount() const
Model set count.
int setModelSet(const swift::misc::simulation::CAircraftModelList &models, const swift::misc::simulation::CSimulatorInfo &simulator, bool forced)
Set the models we want to use.
bool setSetup(const swift::misc::simulation::CAircraftMatcherSetup &setup)
Set the setup.
swift::misc::simulation::CAircraftModel getClosestMatch(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft, swift::misc::simulation::MatchingLog whatToLog, swift::misc::CStatusMessageList *log, bool useMatchingScript) const
Get the closest matching aircraft model from set. Result depends on setup.
virtual swift::misc::simulation::CAircraftModelList getModelSet() const
Get the model set models.
bool isShuttingDown() const
Is application shutting down?
CWebDataServices * getWebDataServices() const
Get the web data services.
QStringList getModelStrings(bool sort=false) const
Model strings.
static QTabWidget * parentTabWidget(QWidget *widget, int maxLevels=5)
From a given widget try to find parent tab widget (where widget is embedded)
Definition: guiutility.cpp:561
Model matcher testing and configuration.
void setWorkbenchView(views::CAircraftModelView *workbenchView)
Set an external.
void tabIndexChanged(int index)
Tab (where this component is embedded) has been changed.
swift::misc::simulation::CAircraftMatcherSetup getMatchingSetup() const
Get setup.
void setMatchingSetup(const swift::misc::simulation::CAircraftMatcherSetup &setup)
Set the setup.
void changed(const swift::misc::simulation::CSimulatorInfo &simulator)
Value has been changed.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
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.
Status messages, e.g. from Core -> GUI.
Value object for ICAO classification.
Value object for ICAO classification.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
Value object encapsulating information about an airpot.
Definition: livery.h:29
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
Value object encapsulating information of a user.
Definition: user.h:28
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
Value object encapsulating a list of aircraft models.
CSimulatorInfo simulatorsWithMaxEntries() const
Which simulator(s) have the most entries?
Comprehensive information of an aircraft.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool isSingleSimulator() const
Single simulator selected.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
High level reusable GUI components.
Definition: aboutdialog.cpp:13
Models to be used with views, mainly QTableView.
Views, mainly QTableView.
Free functions in swift::misc.
bool runScriptAndModified() const
Did run the script with modified result.