swift
contextsimulatorimpl.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 <QMetaObject>
7 #include <QPointer>
8 #include <QStringList>
9 #include <QThread>
10 #include <Qt>
11 #include <QtGlobal>
12 
13 #include "config/buildconfig.h"
14 #include "core/application.h"
20 #include "core/corefacade.h"
21 #include "core/db/databaseutils.h"
23 #include "core/simulator.h"
24 #include "misc/aviation/callsign.h"
25 #include "misc/dbusserver.h"
26 #include "misc/logcategories.h"
27 #include "misc/loghandler.h"
28 #include "misc/logmessage.h"
30 #include "misc/pq/units.h"
35 #include "misc/statusmessage.h"
36 #include "misc/threadutils.h"
37 #include "misc/verify.h"
38 
39 using namespace swift::config;
40 using namespace swift::core::db;
41 using namespace swift::misc;
42 using namespace swift::misc::physical_quantities;
43 using namespace swift::misc::aviation;
44 using namespace swift::misc::network;
45 using namespace swift::misc::simulation;
46 using namespace swift::misc::simulation::xplane;
47 using namespace swift::misc::geo;
48 using namespace swift::misc::weather;
49 using namespace swift::misc::simulation;
50 using namespace swift::misc::simulation::settings;
51 using namespace swift::misc::simulation::data;
52 using namespace std::chrono_literals;
53 
54 namespace swift::core::context
55 {
56  CContextSimulator::CContextSimulator(CCoreFacadeConfig::ContextMode mode, CCoreFacade *runtime)
57  : IContextSimulator(mode, runtime), CIdentifiable(this), m_plugins(new CPluginManagerSimulator(this))
58  {
59  this->setObjectName("CContextSimulator");
61 
62  Q_ASSERT_X(sApp, Q_FUNC_INFO, "Need sApp");
63  MatchingLog logMatchingMessages =
64  CBuildConfig::isLocalDeveloperDebugBuild() ? MatchingLogAll : MatchingLogSimplified;
65  m_logMatchingMessages = logMatchingMessages;
66  m_plugins->collectPlugins();
67  this->restoreSimulatorPlugins();
68 
69  connect(&m_aircraftMatcher, &CAircraftMatcher::setupChanged, this, &CContextSimulator::matchingSetupChanged);
70  connect(&CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance(),
71  &CCentralMultiSimulatorModelSetCachesProvider::cacheChanged, this, &CContextSimulator::modelSetChanged);
72 
73  // deferred init of last model set, if no other data are set in meantime
74  const QPointer<CContextSimulator> myself(this);
75  QTimer::singleShot(2500ms, this, [=] {
76  if (!myself) { return; }
77  this->initByLastUsedModelSet();
78  m_aircraftMatcher.setSetup(m_matchingSettings.get());
79  if (m_aircraftMatcher.getModelSetCount() <= MatchingLogMaxModelSetSize)
80  {
81  this->enableMatchingMessages(logMatchingMessages);
82  }
83  });
84 
85  // Validation
86  m_validator = new CBackgroundValidation(this);
87  this->setValidator(this->getSimulatorPluginInfo().getSimulator());
88  connect(this, &CContextSimulator::simulatorChanged, this, &CContextSimulator::setValidator);
90  Qt::QueuedConnection);
91 
92  m_validator->start(QThread::LowestPriority);
93  using namespace std::chrono_literals;
94  m_validator->startUpdating(60s);
95  }
96 
97  // For validation we need simulator directory and model directory
98  // this function is called at start (simulator=0) and when there is an active connection to a simulator
99  void CContextSimulator::setValidator(const CSimulatorInfo &simulator)
100  {
101  if (simulator.isSingleSimulator())
102  {
103  const QString simDir = m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator);
104  const QStringList modelDirList = m_multiSimulatorSettings.getModelDirectoriesOrDefault(simulator);
105  m_validator->setCurrentSimulator(simulator, simDir, modelDirList);
106  }
107  else { m_validator->setCurrentSimulator(CSimulatorInfo::None, {}, {}); }
108  }
109 
111  {
112  return m_simulatorPlugin.second && IContextSimulator::isSimulatorAvailable();
113  }
114 
116 
118  {
119  if (m_validator)
120  {
121  disconnect(m_validator);
122  m_validator->quitAndWait();
123  m_validator->deleteLater();
124  m_validator = nullptr;
125  }
126  this->stopSimulatorListeners();
127  this->disconnect();
128  this->unloadSimulatorPlugin();
129 
130  // give listeners a head start
131  // if simconnect is running remotely it can take a while until it shutdowns
132  m_listenersThread.quit();
133  m_listenersThread.wait(10 * 1000);
134  }
135 
137  {
138  if (!m_plugins) { return {}; }
139  return m_plugins->getAvailableSimulatorPlugins();
140  }
141 
143  {
145  if (!p.isValid()) { return {}; }
146  const CSimulatorInfo sim = p.getSimulatorInfo();
147  if (!sim.isSingleSimulator()) { return {}; }
148  return m_multiSimulatorSettings.getSettings(sim);
149  }
150 
152  {
153  if (!simulator.isSingleSimulator()) { return false; }
154  const CSimulatorSettings simSettings = m_multiSimulatorSettings.getSettings(simulator);
155  if (simSettings == settings) { return false; }
156  const CStatusMessage msg = m_multiSimulatorSettings.setSettings(settings, simulator);
158  emit this->simulatorSettingsChanged();
159  return true;
160  }
161 
163  {
164  return this->listenForSimulator(simulatorInfo);
165  }
166 
168  {
169  if (!m_simulatorPlugin.first.isUnspecified() && m_simulatorPlugin.first == simulatorInfo)
170  {
171  this->unloadSimulatorPlugin();
172  }
173 
174  ISimulatorListener *listener = m_plugins->getListener(simulatorInfo.getIdentifier());
175  Q_ASSERT(listener);
176  QMetaObject::invokeMethod(listener, &ISimulatorListener::stop, Qt::QueuedConnection);
177  }
178 
180  {
181  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
182  if (!m_plugins) { return 0; }
183  return m_plugins->checkAvailableListeners();
184  }
185 
186  ISimulator::SimulatorStatus CContextSimulator::getSimulatorStatus() const
187  {
188  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
189  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return ISimulator::Unspecified; }
190  return m_simulatorPlugin.second->getSimulatorStatus();
191  }
192 
194  {
195  static const CSimulatorPluginInfo unspecified;
196  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
197  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return unspecified; }
198  if (m_simulatorPlugin.first.getSimulator().contains("emulated", Qt::CaseInsensitive))
199  {
200  return m_simulatorPlugin.second->getSimulatorPluginInfo();
201  }
202  return m_simulatorPlugin.first;
203  }
204 
206  {
207  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
208  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CSimulatorInternals(); }
209  return m_simulatorPlugin.second->getSimulatorInternals();
210  }
211 
213  {
214  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
216  if (!simulator.isSingleSimulator()) { return CAircraftModelList(); }
217 
218  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
219  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
220  }
221 
223  {
224  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
225  if (m_simulatorPlugin.second)
226  {
227  if (m_simulatorPlugin.second->isConnected())
228  {
229  if (m_simulatorPlugin.second->isEmulatedDriver())
230  {
231  // specialized version returning the "emulated info"
232  return this->getSimulatorPluginInfo().getSimulatorInfo();
233  }
234  return m_simulatorPlugin.first.getSimulatorInfo();
235  }
236  }
237  return m_modelSetSimulator.get();
238  }
239 
241  {
242  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
243  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
244  if (this->isSimulatorAvailable()) { return; } // if a plugin is loaded, do ignore this
245  m_modelSetSimulator.set(simulator);
246  const CAircraftModelList models = this->getModelSet(); // cache synced
247  m_aircraftMatcher.setModelSet(models, simulator, false);
248  }
249 
251  {
252  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
253  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().simulatorsWithInitializedCache();
254  }
255 
257  {
258  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
259  CStatusMessageList msgs;
260  const CSimulatorInfo simulators = this->simulatorsWithInitializedModelSet();
261  if (simulators.isNoSimulator())
262  {
263  msgs.push_back(CStatusMessage(this).validationError(
264  u"No model set so far, you need at least one model set. Hint: You can create a model set in the "
265  u"mapping tool, or copy an existing set in the launcher."));
266  }
267  return msgs;
268  }
269 
271  {
272  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
273  return this->getModelSet().getModelStringList(false);
274  }
275 
276  bool CContextSimulator::isKnownModelInSet(const QString &modelString) const
277  {
278  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
279  const bool known = this->getModelSet().containsModelString(modelString);
280  return known;
281  }
282 
284  {
285  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
286  if (removeModels.isEmpty()) { return 0; }
287  const CSimulatorInfo simulator = m_modelSetSimulator.get();
288  if (!simulator.isSingleSimulator()) { return 0; }
289 
290  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
291  CAircraftModelList models =
292  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
293  const int removed = models.removeModelsWithString(removeModels, Qt::CaseInsensitive);
294  if (removed > 0)
295  {
296  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().setCachedModels(models, simulator);
297  }
298  return removed;
299  }
300 
301  QStringList CContextSimulator::getModelSetCompleterStrings(bool sorted) const
302  {
303  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << sorted; }
304  return this->getModelSet().toCompleterStrings(sorted);
305  }
306 
308  {
309  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
310  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return 0; }
311  return this->getModelSet().size();
312  }
313 
314  void CContextSimulator::disableModelsForMatching(const CAircraftModelList &removedModels, bool incremental)
315  {
316  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
317  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
318  m_aircraftMatcher.disableModelsForMatching(removedModels, incremental);
319  }
320 
322  {
323  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
324  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CAircraftModelList(); }
325  return m_aircraftMatcher.getDisabledModelsForMatching();
326  }
327 
329  {
330  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
331  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
332  m_aircraftMatcher.restoreDisabledModels();
333  }
334 
336  {
337  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
338  if (!m_validator) { return false; }
339  return m_validator->isValidating();
340  }
341 
343  {
344  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
345  if (!m_validator) { return false; }
346  const QString simDir =
347  simulator.isSingleSimulator() ? m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator) : "";
348  return m_validator->triggerValidation(simulator, simDir);
349  }
350 
352  {
353  if (isDebugEnabled())
354  {
355  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << modelString;
356  }
357  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CAircraftModelList(); }
358 
359  return this->getModelSet().findModelsStartingWith(modelString);
360  }
361 
363  {
364  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
365  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return m_renderSettings.get(); }
366  return m_simulatorPlugin.second->getInterpolationSetupGlobal();
367  }
368 
370  {
371  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
372  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CInterpolationSetupList(); }
373  return m_simulatorPlugin.second->getInterpolationSetupsPerCallsign();
374  }
375 
378  {
379  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
380  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
381  {
383  }
384  return m_simulatorPlugin.second->getInterpolationSetupPerCallsignOrDefault(callsign);
385  }
386 
387  bool CContextSimulator::setInterpolationAndRenderingSetupsPerCallsign(const CInterpolationSetupList &setups,
388  bool ignoreSameAsGlobal)
389  {
390  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
391  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return false; }
392  return m_simulatorPlugin.second->setInterpolationSetupsPerCallsign(setups, ignoreSameAsGlobal);
393  }
394 
396  {
397  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup; }
398 
399  // anyway save for future reference
400  const CStatusMessage m = m_renderSettings.setAndSave(setup);
402 
403  // transfer to sim
404  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
405  m_simulatorPlugin.second->setInterpolationSetupGlobal(setup);
406  }
407 
409  {
410  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
411  if (callsign.isEmpty()) { return CStatusMessageList(); }
412  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CStatusMessageList(); }
413  return m_simulatorPlugin.second->getInterpolationMessages(callsign);
414  }
415 
416  bool CContextSimulator::loadSimulatorPlugin(const CSimulatorPluginInfo &simulatorPluginInfo)
417  {
418  Q_ASSERT(this->getIContextApplication());
420  Q_ASSERT(!simulatorPluginInfo.isUnspecified());
421  Q_ASSERT(CThreadUtils::thisIsMainThread()); // only run in main thread
422 
423  // Is a plugin already loaded?
424  if (!m_simulatorPlugin.first.isUnspecified())
425  {
426  // This can happen, if a listener emitted simulatorStarted twice or two different simulators
427  // are running at the same time. In this case, we leave the loaded plugin and just return.
428  return false;
429  }
430 
431  if (!simulatorPluginInfo.isValid())
432  {
433  CLogMessage(this).error(u"Illegal plugin");
434  return false;
435  }
436 
437  ISimulatorFactory *factory = m_plugins->getFactory(simulatorPluginInfo.getIdentifier());
438  Q_ASSERT_X(factory, Q_FUNC_INFO, "no factory");
439 
440  // We assume we run in the same process as the own aircraft context
441  // Hence we pass in memory reference to own aircraft object
442  Q_ASSERT_X(this->getIContextOwnAircraft()->isUsingImplementingObject(), Q_FUNC_INFO,
443  "Need implementing object");
444  Q_ASSERT_X(this->getIContextNetwork()->isUsingImplementingObject(), Q_FUNC_INFO, "Need implementing object");
445  IOwnAircraftProvider *ownAircraftProvider = this->getRuntime()->getCContextOwnAircraft();
446  IRemoteAircraftProvider *renderedAircraftProvider = this->getRuntime()->getCContextNetwork();
447  IClientProvider *clientProvider = this->getRuntime()->getCContextNetwork();
448  ISimulator *simulator =
449  factory->create(simulatorPluginInfo, ownAircraftProvider, renderedAircraftProvider, clientProvider);
450  Q_ASSERT_X(simulator, Q_FUNC_INFO, "no simulator driver can be created");
451 
452  this->setRemoteAircraftProvider(renderedAircraftProvider);
453 
454  // use simulator info from ISimulator as it can access the emulated driver settings
455  CSimulatorInfo simInfo = simulator->getSimulatorInfo();
456  if (!simInfo.isSingleSimulator())
457  {
458  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Invalid simulator from plugin");
459  simInfo = CSimulatorInfo::p3d();
460  if (simulator->isEmulatedDriver())
461  {
462  CLogMessage(this).error(
463  u"Emulated driver does NOT provide valid simulator, using default '%1', plugin: '%2'")
464  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
465  }
466  else
467  {
468  CLogMessage(this).error(u"Invalid driver using default '%1', plugin: '%2'")
469  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
470  }
471  }
472  Q_ASSERT_X(simInfo.isSingleSimulator(), Q_FUNC_INFO, "need single simulator");
473 
474  m_modelSetSimulator.set(simInfo);
475  const CAircraftModelList modelSetModels = this->getModelSet(); // synced
476  m_aircraftMatcher.setModelSet(modelSetModels, simInfo, true);
477  m_aircraftMatcher.setDefaultModel(simulator->getDefaultModel());
478 
479  bool c =
480  connect(simulator, &ISimulator::simulatorStatusChanged, this, &CContextSimulator::onSimulatorStatusChanged);
481  Q_ASSERT(c);
483  &CContextSimulator::onAddingRemoteAircraftFailed);
484  Q_ASSERT(c);
486  &CContextSimulator::onOwnSimulatorModelChanged);
487  Q_ASSERT(c);
490  Q_ASSERT(c);
493  Q_ASSERT(c);
496  Q_ASSERT(c);
498  Q_ASSERT(c);
500  Q_ASSERT(c);
502  Q_ASSERT(c);
503 
504  // disconnect for X-Plane FPS below 20
505  c = connect(simulator, &ISimulator::insufficientFrameRateDetected, this, [this](bool fatal) {
506  if (fatal) { emit this->vitalityLost(); }
507  });
508  Q_ASSERT(c);
511  Q_ASSERT(c);
512 
513  // log from context to simulator
515  &CContextSimulator::relayStatusMessageToSimulator);
516  Q_ASSERT(c);
518  &CContextSimulator::relayStatusMessageToSimulator);
519  Q_ASSERT(c);
520  Q_UNUSED(c)
521 
522  // Once the simulator signaled it is ready to simulate, add all known aircraft
523  m_initallyAddAircraft = true;
524  m_wasSimulating = false;
525  m_matchingMessages.clear();
526  m_failoverAddingCounts.clear();
527 
528  // try to connect to simulator
529  const bool connected = simulator->connectTo();
530  if (!connected)
531  {
532  CLogMessage(this).error(u"Simulator plugin connection to simulator '%1' failed")
533  << simulatorPluginInfo.toQString(true);
534  return false;
535  }
536 
537  // when everything is set up connected, update the current plugin info
538  m_simulatorPlugin.first = simulatorPluginInfo;
539  m_simulatorPlugin.second = simulator;
540  m_simulatorPlugin.second->setInterpolationSetupGlobal(m_renderSettings.get());
541 
542  // Emit signal after this function completes completely decoupled
543  QPointer<CContextSimulator> myself(this);
544  QTimer::singleShot(25, this, [=] {
545  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
546  if (m_simulatorPlugin.second)
547  {
548  CLogMessage(this).info(u"Simulator plugin loaded: '%1' connected: %2")
549  << simulatorPluginInfo.toQString(true) << boolToYesNo(connected);
550 
551  // use the real driver as this will also work eith emulated driver
552  emit this->simulatorPluginChanged(m_simulatorPlugin.second->getSimulatorPluginInfo());
553  emit this->simulatorChanged(m_simulatorPlugin.second->getSimulatorInfo());
554  }
555  });
556 
557  return true;
558  }
559 
560  bool CContextSimulator::listenForSimulator(const CSimulatorPluginInfo &simulatorInfo)
561  {
562  Q_ASSERT(this->getIContextApplication());
564  Q_ASSERT(!simulatorInfo.isUnspecified());
565  Q_ASSERT(m_simulatorPlugin.first.isUnspecified());
566 
567  if (!m_listenersThread.isRunning())
568  {
569  m_listenersThread.setObjectName("CContextSimulator: Thread for listener " + simulatorInfo.getIdentifier());
570  m_listenersThread.start(QThread::LowPriority);
571  }
572 
573  ISimulatorListener *listener = m_plugins->createListener(simulatorInfo.getIdentifier());
574  if (!listener) { return false; }
575 
576  if (listener->thread() != &m_listenersThread)
577  {
578  Q_ASSERT_X(!listener->parent(), Q_FUNC_INFO, "Objects with parent cannot be moved to thread");
579 
580  const bool c = connect(listener, &ISimulatorListener::simulatorStarted, this,
581  &CContextSimulator::onSimulatorStarted, Qt::QueuedConnection);
582  if (!c)
583  {
584  CLogMessage(this).error(u"Unable to use '%1'") << simulatorInfo.toQString();
585  return false;
586  }
587  listener->setProperty("isInitialized", true);
588  listener->moveToThread(&m_listenersThread);
589  }
590 
591  // start if not already running
592  if (!listener->isRunning())
593  {
594  const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::start, Qt::QueuedConnection);
595  Q_ASSERT_X(s, Q_FUNC_INFO, "cannot invoke method");
596  Q_UNUSED(s)
597  }
598  CLogMessage(this).info(u"Listening for simulator '%1'") << simulatorInfo.getIdentifier();
599  return true;
600  }
601 
602  void CContextSimulator::listenForAllSimulators()
603  {
604  const auto plugins = getAvailableSimulatorPlugins();
605  for (const CSimulatorPluginInfo &p : plugins)
606  {
607  Q_ASSERT(!p.isUnspecified());
608  if (p.isValid()) { this->listenForSimulator(p); }
609  }
610  }
611 
612  void CContextSimulator::unloadSimulatorPlugin()
613  {
614  if (!m_simulatorPlugin.first.isUnspecified())
615  {
616  ISimulator *simulator = m_simulatorPlugin.second;
617  if (simulator && simulator->isConnected())
618  {
619  // we are about to unload an connected simulator
620  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
622  }
623 
624  m_simulatorPlugin.second = nullptr;
625  m_simulatorPlugin.first = CSimulatorPluginInfo();
626 
627  Q_ASSERT(this->getIContextNetwork());
628  Q_ASSERT(this->getIContextNetwork()->isLocalObject());
629 
630  // unload and disconnect
631  if (simulator)
632  {
633  // disconnect signals and delete
634  simulator->disconnect(this);
635  simulator->unload();
636  simulator->deleteLater();
638  emit this->simulatorChanged(CSimulatorInfo());
639  }
640 
641  if (m_wasSimulating) { emit this->vitalityLost(); }
642  m_wasSimulating = false;
643  }
644  }
645 
646  void CContextSimulator::xCtxAddedRemoteAircraftReadyForModelMatching(const CSimulatedAircraft &remoteAircraft)
647  {
648  if (!this->isSimulatorPluginAvailable()) { return; }
649 
650  const CCallsign callsign = remoteAircraft.getCallsign();
651  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Remote aircraft with empty callsign");
652  if (callsign.isEmpty()) { return; }
653 
654  // here we find the best simulator model for a resolved model
655  // in the first step we already tried to find accurate ICAO codes etc.
656  // coming from CAirspaceMonitor::sendReadyForModelMatching
657  MatchingLog whatToLog = m_logMatchingMessages;
658  CStatusMessageList matchingMessages;
659  CStatusMessageList *pMatchingMessages = m_logMatchingMessages > 0 ? &matchingMessages : nullptr;
660  CAircraftModel aircraftModel =
661  m_aircraftMatcher.getClosestMatch(remoteAircraft, whatToLog, pMatchingMessages, true);
662  Q_ASSERT_X(remoteAircraft.getCallsign() == aircraftModel.getCallsign(), Q_FUNC_INFO, "Mismatching callsigns");
663 
664  // decide CG
665  const CLength cgModel = aircraftModel.getCG();
666  const CLength cgSim = m_simulatorPlugin.second->getSimulatorCGPerModelString(aircraftModel.getModelString());
667  const CSimulatorSettings simSettings = this->getSimulatorSettings();
668  switch (simSettings.getCGSource())
669  {
670  case CSimulatorSettings::CGFromSimulatorOnly: aircraftModel.setCG(cgSim); break;
671  case CSimulatorSettings::CGFromSimulatorFirst:
672  if (!cgSim.isNull()) { aircraftModel.setCG(cgSim); }
673  break;
674  case CSimulatorSettings::CGFromDBFirst:
675  if (cgModel.isNull()) { aircraftModel.setCG(cgSim); }
676  break;
677  // case CSimulatorSettings::CGFromDBOnly:
678  default: break; // leave CG from model alone
679  }
680 
681  const CLength overriddenCG =
682  m_simulatorPlugin.second->overriddenCGorDefault(CLength::null(), aircraftModel.getModelString());
683  if (!overriddenCG.isNull()) { aircraftModel.setCG(overriddenCG); }
684 
685  // model in provider
686  this->updateAircraftModel(callsign, aircraftModel, this->identifier());
687 
688  const CSimulatedAircraft aircraftAfterModelApplied =
689  this->getAircraftInRangeForCallsign(remoteAircraft.getCallsign());
690  if (!aircraftAfterModelApplied.hasModelString())
691  {
692  if (!aircraftAfterModelApplied.hasCallsign()) { return; } // removed
693  if (this->isAircraftInRange(aircraftAfterModelApplied.getCallsign()))
694  {
695  return;
696  } // removed, but callsig, we did crosscheck
697 
698  // callsign, but no model string
699  CLogMessage(this).error(u"Matching error for '%1', no model string")
700  << aircraftAfterModelApplied.getCallsign().asString();
701 
702  CSimulatedAircraft brokenAircraft(aircraftAfterModelApplied);
703  brokenAircraft.setEnabled(false);
704  brokenAircraft.setRendered(false);
705  CCallsign::addLogDetailsToList(
706  pMatchingMessages, callsign,
707  QStringLiteral("Cannot add remote aircraft, no model string: '%1'").arg(brokenAircraft.toQString()));
708  emit this->aircraftRenderingChanged(brokenAircraft);
709  return;
710  }
711 
712  // here the model is added to the simulator
713  m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraftAfterModelApplied);
714  CCallsign::addLogDetailsToList(
715  pMatchingMessages, callsign,
716  QStringLiteral("Logically added remote aircraft: %1").arg(aircraftAfterModelApplied.toQString()));
717 
718  this->clearMatchingMessages(callsign);
719  this->addMatchingMessages(callsign, matchingMessages);
720 
721  // done
722  emit this->modelMatchingCompleted(aircraftAfterModelApplied);
723  }
724 
725  void CContextSimulator::xCtxRemovedRemoteAircraft(const CCallsign &callsign)
726  {
727  if (!this->isSimulatorAvailable()) { return; }
728  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
729  m_failoverAddingCounts.remove(callsign);
730  }
731 
732  void CContextSimulator::onSimulatorStatusChanged(ISimulator::SimulatorStatus status)
733  {
734  m_wasSimulating = m_wasSimulating || status.testFlag(ISimulator::Simulating);
735  if (m_initallyAddAircraft && status.testFlag(ISimulator::Simulating))
736  {
737  // use network to initally add aircraft
738  IContextNetwork *networkContext = this->getIContextNetwork();
739  Q_ASSERT_X(networkContext, Q_FUNC_INFO, "Need context");
740  Q_ASSERT_X(networkContext->isLocalObject(), Q_FUNC_INFO, "Need local object");
741 
742  // initially add aircraft
743  const CSimulatedAircraftList aircraft = networkContext->getAircraftInRange();
744  for (const CSimulatedAircraft &simulatedAircraft : aircraft)
745  {
746  SWIFT_VERIFY_X(!simulatedAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "Need callsign");
747  this->xCtxAddedRemoteAircraftReadyForModelMatching(simulatedAircraft);
748  }
749  m_initallyAddAircraft = false;
750  }
751 
752  if (!status.testFlag(ISimulator::Connected))
753  {
754  // we got disconnected, plugin no longer needed
755  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
756  this->unloadSimulatorPlugin();
757  this->restoreSimulatorPlugins();
758 
759  if (m_wasSimulating) { emit this->vitalityLost(); }
760  m_wasSimulating = false;
761  }
762 
763  emit this->simulatorStatusChanged(status);
764  }
765 
766  void CContextSimulator::xCtxTextMessagesReceived(const network::CTextMessageList &textMessages)
767  {
768  if (!this->isSimulatorAvailable()) { return; }
769  if (!this->getIContextOwnAircraft()) { return; }
770 
771  const CSimulatorMessagesSettings settings = m_messageSettings.getThreadLocal();
772  const CSimulatedAircraft ownAircraft = this->getIContextOwnAircraft()->getOwnAircraft();
773  for (const auto &tm : textMessages)
774  {
775  if (!settings.relayThisTextMessage(tm, ownAircraft)) { continue; }
776  m_simulatorPlugin.second->displayTextMessage(tm);
777  }
778  }
779 
780  void CContextSimulator::xCtxChangedRemoteAircraftModel(const CSimulatedAircraft &aircraft,
781  const swift::misc::CIdentifier &originator)
782  {
783  if (CIdentifiable::isMyIdentifier(originator)) { return; }
784  if (!this->isSimulatorAvailable()) { return; }
785  m_simulatorPlugin.second->changeRemoteAircraftModel(aircraft);
786  }
787 
788  void CContextSimulator::xCtxChangedOwnAircraftModel(const CAircraftModel &aircraftModel,
789  const CIdentifier &originator)
790  {
791  if (CIdentifiable::isMyIdentifier(originator)) { return; }
792  if (!this->isSimulatorAvailable()) { return; }
793 
794  emit this->ownAircraftModelChanged(aircraftModel);
795  }
796 
797  void CContextSimulator::xCtxChangedRemoteAircraftEnabled(const CSimulatedAircraft &aircraft)
798  {
799  if (!this->isSimulatorAvailable()) { return; }
800  m_simulatorPlugin.second->changeRemoteAircraftEnabled(aircraft);
801  }
802 
803  void CContextSimulator::xCtxNetworkConnectionStatusChanged(const CConnectionStatus &from,
804  const CConnectionStatus &to)
805  {
806  Q_UNUSED(from)
807  SWIFT_VERIFY_X(this->getIContextNetwork(), Q_FUNC_INFO, "Missing network context");
808  if (to.isConnected() && this->getIContextNetwork())
809  {
810  m_networkSessionId = this->getIContextNetwork()->getConnectedServer().getServerSessionId(false);
811  if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
812  {
813  m_simulatorPlugin.second->setFlightNetworkConnected(true);
814  }
815  }
816  else if (to.isDisconnected())
817  {
818  m_networkSessionId.clear();
819  m_aircraftMatcher.clearMatchingStatistics();
820  m_matchingMessages.clear();
821  m_failoverAddingCounts.clear();
822 
823  if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
824  {
825  m_simulatorPlugin.second->removeAllRemoteAircraft(); // also removes aircraft
826  m_simulatorPlugin.second->setFlightNetworkConnected(false);
827  }
828  }
829  }
830 
831  void CContextSimulator::onAddingRemoteAircraftFailed(const CSimulatedAircraft &remoteAircraft, bool disabled,
832  bool requestFailover, const CStatusMessage &message)
833  {
834  if (!this->isSimulatorAvailable()) { return; }
835  if (disabled) { m_aircraftMatcher.addingRemoteModelFailed(remoteAircraft); }
836 
837  const CCallsign cs = remoteAircraft.getCallsign();
838  if (cs.isEmpty()) { return; }
839 
840  bool failover = requestFailover;
841  if (requestFailover)
842  {
843  const CAircraftMatcherSetup setup = m_aircraftMatcher.getSetup();
844  if (setup.doModelAddFailover())
845  {
846  const int trial = m_failoverAddingCounts.value(cs, 0);
847  if (trial < MaxModelAddedFailoverTrials)
848  {
849  m_failoverAddingCounts[cs] = trial + 1;
850  this->doMatchingAgain(cs); // has its own single shot logic
851  }
852  else
853  {
854  failover = false;
855  CLogMessage(this).warning(u"Model for '%1' failed adding, tried %2 time(s) to resolve, giving up")
856  << cs.toQString(true) << (trial + 1);
857  }
858  }
859  }
860  emit this->addingRemoteModelFailed(remoteAircraft, disabled, failover, message);
861  }
862 
863  void CContextSimulator::xCtxUpdateSimulatorCockpitFromContext(const CSimulatedAircraft &ownAircraft,
864  const CIdentifier &originator)
865  {
866  if (!this->isSimulatorAvailable()) { return; }
867  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
868 
869  // update
870  m_simulatorPlugin.second->updateOwnSimulatorCockpit(ownAircraft, originator);
871  }
872 
873  void CContextSimulator::xCtxUpdateSimulatorSelcalFromContext(const CSelcal &selcal, const CIdentifier &originator)
874  {
875  if (!this->isSimulatorAvailable()) { return; }
876  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
877 
878  // update
879  m_simulatorPlugin.second->updateOwnSimulatorSelcal(selcal, originator);
880  }
881 
882  void CContextSimulator::xCtxNetworkRequestedNewAircraft(const CCallsign &callsign, const QString &aircraftIcao,
883  const QString &airlineIcao, const QString &livery)
884  {
885  if (m_networkSessionId.isEmpty()) { return; }
886  m_aircraftMatcher.evaluateStatisticsEntry(m_networkSessionId, callsign, aircraftIcao, airlineIcao, livery);
887  }
888 
889  void CContextSimulator::relayStatusMessageToSimulator(const swift::misc::CStatusMessage &message)
890  {
891  if (!this->isSimulatorAvailable()) { return; }
892  if (!sApp || sApp->isShuttingDown()) { return; }
893  const CSimulatorMessagesSettings simMsg = m_messageSettings.getThreadLocal();
894  if (simMsg.relayThisStatusMessage(message) && m_simulatorPlugin.second)
895  {
896  m_simulatorPlugin.second->displayStatusMessage(message);
897  }
898  }
899 
900  void CContextSimulator::changeEnabledSimulators()
901  {
902  CSimulatorPluginInfo currentPluginInfo = m_simulatorPlugin.first;
903  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
904 
905  // Unload the current plugin, if it is no longer enabled
906  if (!currentPluginInfo.isUnspecified() && !enabledSimulators.contains(currentPluginInfo.getIdentifier()))
907  {
908  unloadSimulatorPlugin();
910  }
911  restoreSimulatorPlugins();
912  }
913 
914  void CContextSimulator::restoreSimulatorPlugins()
915  {
916  if (!m_simulatorPlugin.first.isUnspecified()) { return; }
917 
918  this->stopSimulatorListeners();
919  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
920  const CSimulatorPluginInfoList allSimulators = m_plugins->getAvailableSimulatorPlugins();
921  for (const CSimulatorPluginInfo &s : allSimulators)
922  {
923  if (enabledSimulators.contains(s.getIdentifier())) { startSimulatorPlugin(s); }
924  }
925  }
926 
928  {
929  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
930  return m_matchingMessages[callsign];
931  }
932 
934  {
935  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
936  return m_logMatchingMessages;
937  }
938 
940  {
941  if (isDebugEnabled())
942  {
943  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << matchingLogToString(enabled);
944  }
945  if (m_logMatchingMessages == enabled) { return; }
946  m_logMatchingMessages = enabled;
948  }
949 
951  {
952  if (isDebugEnabled())
953  {
954  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << missingOnly;
955  }
956  const CMatchingStatistics statistics = m_aircraftMatcher.getCurrentStatistics();
957  return missingOnly ? statistics.findMissingOnly() : statistics;
958  }
959 
961  {
962  if (isDebugEnabled())
963  {
964  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup.toQString();
965  }
966  m_aircraftMatcher.setSetup(setup);
967  const CStatusMessage msg = m_matchingSettings.setAndSave(setup);
969  }
970 
972  {
973  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
974  return m_aircraftMatcher.getSetup();
975  }
976 
978  {
979  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
980  bool added = add;
981  if (add)
982  {
983  m_simulatorPlugin.second->setTestMode(true);
984  added = m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraft);
985  }
986  else
987  {
988  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(aircraft.getCallsign());
989  m_simulatorPlugin.second->setTestMode(false); // AFTER we have removed it
990  }
991  return added;
992  }
993 
995  const CAircraftParts &parts)
996  {
997  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
998  CAircraftSituation s = situation; // make sure to have correct callsign
999  s.setCallsign(cs);
1000  return m_simulatorPlugin.second->testSendSituationAndParts(cs, s, parts);
1001  }
1002 
1003  bool CContextSimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
1004  {
1005  Q_UNUSED(originator)
1006  if (commandLine.isEmpty()) { return false; }
1007  CSimpleCommandParser parser({
1008  ".plugin", ".drv", ".driver", // forwarded to driver
1009  ".ris" // rendering interpolator setup
1010  });
1011  parser.parse(commandLine);
1012  if (!parser.isKnownCommand()) { return false; }
1013  if (parser.matchesCommand("ris"))
1014  {
1016  const QString p1 = parser.part(1);
1017  if (!parser.hasPart(2)) { return false; }
1018  const bool on = stringToBool(parser.part(2));
1019  if (p1 == "debug") { rs.setSimulatorDebuggingMessages(on); }
1020  else if (p1 == "parts") { rs.setEnabledAircraftParts(on); }
1021  else { return false; }
1023  CLogMessage(this, CLogCategories::cmdLine()).info(u"Setup is: '%1'") << rs.toQString(true);
1024  return true;
1025  }
1026  if (parser.matchesCommand("plugin") || parser.matchesCommand("drv") || parser.matchesCommand("driver"))
1027  {
1028  if (!m_simulatorPlugin.second) { return false; }
1029  return m_simulatorPlugin.second->parseCommandLine(commandLine, originator);
1030  }
1031  return false;
1032  }
1033 
1034  QPointer<ISimulator> CContextSimulator::simulator() const
1035  {
1036  if (!this->isSimulatorAvailable() || !m_simulatorPlugin.second) { return nullptr; }
1037  return m_simulatorPlugin.second;
1038  }
1039 
1041  {
1042  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
1043  if (!m_simulatorPlugin.second) { return false; }
1044  return m_simulatorPlugin.second->followAircraft(callsign);
1045  }
1046 
1048  {
1049  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1050  if (!m_simulatorPlugin.second) { return; }
1051  m_simulatorPlugin.second->recalculateAllAircraft();
1052  }
1053 
1055  {
1056  CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1057  if (aircraft.getCallsign() != callsign) { return false; } // not found
1058  if (!this->isSimulatorAvailable()) { return false; }
1059 
1060  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
1061  aircraft.setModel(aircraft.getNetworkModel()); // like originally from network
1062  QPointer<CContextSimulator> myself(this);
1063  QTimer::singleShot(1000, this, [=] {
1064  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1065  this->xCtxAddedRemoteAircraftReadyForModelMatching(aircraft);
1066  });
1067  return true;
1068  }
1069 
1071  {
1072  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << situation; }
1073  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
1074  return m_simulatorPlugin.second->requestElevationBySituation(situation);
1075  }
1076 
1078  const CLength &range) const
1079  {
1080  if (isDebugEnabled())
1081  {
1083  << Q_FUNC_INFO << reference.convertToQString(true) << range;
1084  }
1085  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return CElevationPlane::null(); }
1086  return m_simulatorPlugin.second->findClosestElevationWithinRange(reference, range);
1087  }
1088 
1090  {
1091  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1092  const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
1093  if (callsigns.isEmpty()) { return 0; }
1094  int delayMs = 25;
1095  QPointer<CContextSimulator> myself(this);
1096  for (const CCallsign &cs : callsigns)
1097  {
1098  QTimer::singleShot(delayMs, this, [=] {
1099  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1100  this->doMatchingAgain(cs);
1101  });
1102  delayMs += 25;
1103  }
1104  return callsigns.size();
1105  }
1106 
1108  {
1109  if (isDebugEnabled())
1110  {
1111  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign.asString();
1112  }
1113  if (!this->isAircraftInRange(callsign)) { return false; }
1114  if (!this->isSimulatorAvailable()) { return false; }
1115 
1116  QPointer<CContextSimulator> myself(this);
1117  QTimer::singleShot(2500, this, [=] {
1118  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1119  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1120  if (!aircraft.hasCallsign()) { return; } // no longer valid
1121  CSimulatedAircraft resetAircraft(aircraft);
1122  resetAircraft.resetToNetworkModel();
1123  resetAircraft.setEnabled(true);
1124  this->xCtxAddedRemoteAircraftReadyForModelMatching(resetAircraft);
1125  });
1126  return true;
1127  }
1128 
1129  void CContextSimulator::onSimulatorStarted(const CSimulatorPluginInfo &info)
1130  {
1131  this->stopSimulatorListeners();
1132  this->loadSimulatorPlugin(info);
1133 
1134  // if we have enabled messages, we will disable if size getting too high
1135  if (m_logMatchingMessages)
1136  {
1137  const QPointer<CContextSimulator> myself(this);
1138  QTimer::singleShot(5000, this, [=] {
1139  if (!myself) { return; }
1140  if (m_aircraftMatcher.getModelSetCount() > MatchingLogMaxModelSetSize)
1141  {
1142  const MatchingLog log = CBuildConfig::isDebugBuild() ? MatchingLogAll : MatchingLogSimplified;
1143  this->enableMatchingMessages(log);
1144  }
1145  });
1146  }
1147  }
1148 
1149  void CContextSimulator::onOwnSimulatorModelChanged(const CAircraftModel &model)
1150  {
1151  CAircraftModel lookupModel(model);
1152  if (!lookupModel.isLoadedFromDb()) { lookupModel = this->reverseLookupModel(model); }
1153  emit this->ownAircraftModelChanged(lookupModel);
1154  }
1155 
1156  void CContextSimulator::stopSimulatorListeners()
1157  {
1159  {
1160  ISimulatorListener *listener = m_plugins->getListener(info.getIdentifier());
1161  if (listener)
1162  {
1163  const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::stop);
1164  Q_ASSERT_X(s, Q_FUNC_INFO, "Cannot invoke stop");
1165  Q_UNUSED(s)
1166  }
1167  }
1168  }
1169 
1170  void CContextSimulator::addMatchingMessages(const CCallsign &callsign, const CStatusMessageList &messages)
1171  {
1172  if (callsign.isEmpty()) { return; }
1173  if (messages.isEmpty()) { return; }
1174  if (!m_logMatchingMessages) { return; }
1175  if (m_matchingMessages.contains(callsign))
1176  {
1177  CStatusMessageList &msgs = m_matchingMessages[callsign];
1178  msgs.push_back(messages);
1179  }
1180  else { m_matchingMessages.insert(callsign, messages); }
1181  }
1182 
1183  void CContextSimulator::clearMatchingMessages(const CCallsign &callsign)
1184  {
1185  if (callsign.isEmpty()) { return; }
1186  m_matchingMessages.remove(callsign);
1187  }
1188 
1189  CAircraftModel CContextSimulator::reverseLookupModel(const CAircraftModel &model)
1190  {
1191  bool modified = false;
1192  const CAircraftModel reverseModel =
1193  CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified);
1194  return reverseModel;
1195  }
1196 
1197  void CContextSimulator::initByLastUsedModelSet()
1198  {
1199  // no models in matcher, but in cache, we can set them as default
1200  const CSimulatorInfo simulator(m_modelSetSimulator.get());
1201  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
1202  const CAircraftModelList models(this->getModelSet()); // synced
1203  CLogMessage(this).info(u"Init aircraft matcher with %1 models from set for '%2'")
1204  << models.size() << simulator.toQString();
1205  m_aircraftMatcher.setModelSet(models, simulator, false);
1206  }
1207 } // namespace swift::core::context
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
void setupChanged()
Setup changed.
void evaluateStatisticsEntry(const QString &sessionId, const swift::misc::aviation::CCallsign &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery)
Evaluate if a statistics entry makes sense and add it.
void clearMatchingStatistics()
Clear the statistics.
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.
void addingRemoteModelFailed(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft)
Adding a model failed.
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.
const swift::misc::simulation::CMatchingStatistics & getCurrentStatistics() const
The current statistics.
swift::misc::simulation::CAircraftModelList getDisabledModelsForMatching() const
The disabled models for matching.
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.
void disableModelsForMatching(const swift::misc::simulation::CAircraftModelList &removedModels, bool incremental)
Remove a model for matching.
void restoreDisabledModels()
Restore the models removed with CAircraftMatcher::disableModelForMatching.
bool isShuttingDown() const
Is application shutting down?
ContextMode
How to handle a given context.
The class providing facades (the contexts) for all DBus relevant operations.
Definition: corefacade.h:57
context::CContextNetwork * getCContextNetwork()
Context for network.
Definition: corefacade.cpp:487
context::CContextOwnAircraft * getCContextOwnAircraft()
Context for own aircraft.
Definition: corefacade.cpp:501
Manages plugins for the simulator context.
virtual void collectPlugins()
Looks for all available plugins.
swift::misc::simulation::CSimulatorPluginInfoList getAvailableSimulatorPlugins() const
Get all simulator driver plugins.
ISimulatorListener * createListener(const QString &pluginId)
Create simulator listener from the plugin In case one is existing already, it is returned instead....
int checkAvailableListeners()
Check if simulator is connected.
ISimulatorFactory * getFactory(const QString &pluginId)
Get simulator factory from the plugin.
ISimulatorListener * getListener(const QString &pluginId)
Get previously created simulator listener from the plugin Returns nullptr if listener is not yet crea...
void renderRestrictionsChanged(bool restricted, bool enabled, int maxAircraft, const swift::misc::physical_quantities::CLength &maxRenderedDistance)
Render restrictions have been changed.
void physicallyAddingRemoteModelFailed(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft, bool disabled, bool requestFailover, const swift::misc::CStatusMessage &message)
Adding the remote model failed.
void ownAircraftModelChanged(const swift::misc::simulation::CAircraftModel &model)
Emitted when own aircraft model has changed.
void simulatorStatusChanged(swift::core::ISimulator::SimulatorStatus status)
Simulator combined status.
void insufficientFrameRateDetected(bool fatal)
Frame rate has fallen too far below the threshold to maintain consistent sim rate.
void autoPublishDataWritten(const swift::misc::simulation::CSimulatorInfo &simulator)
Auto publish data written for simulator.
void airspaceSnapshotHandled()
An airspace snapshot was handled.
@ Disconnected
not connected, and hence not simulating/paused
Definition: simulator.h:69
@ Simulating
Is the simulator actually simulating?
Definition: simulator.h:71
@ Connected
Is the plugin connected to the simulator?
Definition: simulator.h:70
@ Unspecified
unspecied, needed as default value
Definition: simulator.h:68
void aircraftRenderingChanged(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Aircraft rendering changed.
void driverMessages(const swift::misc::CStatusMessageList &messages)
Relevant simulator messages to be explicitly displayed.
void interpolationAndRenderingSetupChanged()
Interpolation or rendering setup changed.
Interface to a simulator listener.
Definition: simulator.h:630
void simulatorStarted(const swift::misc::simulation::CSimulatorPluginInfo &info)
Emitted when the listener discovers the simulator running.
void start()
Start listening for the simulator to start.
Definition: simulator.cpp:1265
void stop()
Stops listening.
Definition: simulator.cpp:1282
virtual bool followAircraft(const swift::misc::aviation::CCallsign &callsign)
Follow aircraft im simulator view.
virtual bool setSimulatorSettings(const swift::misc::simulation::settings::CSimulatorSettings &settings, const swift::misc::simulation::CSimulatorInfo &simulator)
Set settings for give simulator.
virtual swift::misc::CStatusMessageList verifyPrerequisites() const
Verify prerequisites for simulation like an existing model set.
virtual bool resetToModelMatchingAircraft(const swift::misc::aviation::CCallsign &callsign)
Reset model by matching it again.
virtual ISimulator::SimulatorStatus getSimulatorStatus() const
Simulator combined status.
virtual void setInterpolationAndRenderingSetupGlobal(const swift::misc::simulation::CInterpolationAndRenderingSetupGlobal &setup)
Set the global setup.
virtual swift::misc::simulation::CAircraftMatcherSetup getMatchingSetup() const
Get matching setup.
virtual swift::misc::simulation::CAircraftModelList getModelSet() const
Installed models in simulator eco system.
virtual bool isKnownModelInSet(const QString &modelString) const
Known model?
virtual swift::misc::simulation::CInterpolationSetupList getInterpolationAndRenderingSetupsPerCallsign() const
Get all setups per callsign.
virtual swift::misc::simulation::CAircraftModelList getModelSetModelsStartingWith(const QString &modelString) const
Models for model string.
virtual swift::misc::geo::CElevationPlane findClosestElevationWithinRange(const swift::misc::geo::CCoordinateGeodetic &reference, const swift::misc::physical_quantities::CLength &range) const
Find closest elevation (or return NULL)
virtual bool startSimulatorPlugin(const swift::misc::simulation::CSimulatorPluginInfo &simulatorInfo)
Load and start specific simulator plugin.
virtual void disableModelsForMatching(const swift::misc::simulation::CAircraftModelList &removedModels, bool incremental)
Remove a model for matching.
virtual swift::misc::simulation::CSimulatorInternals getSimulatorInternals() const
Simulator setup.
virtual swift::misc::CStatusMessageList getMatchingMessages(const swift::misc::aviation::CCallsign &callsign) const
Get mapping messages.
virtual QStringList getModelSetCompleterStrings(bool sorted) const
Model set completer string.
virtual bool triggerModelSetValidation(const swift::misc::simulation::CSimulatorInfo &simulator)
Trigger model set validation.
virtual int removeModelsFromSet(const swift::misc::simulation::CAircraftModelList &removeModels)
Remove models from set.
static void registerHelp()
Register dot commands.
virtual bool testUpdateRemoteAircraft(const swift::misc::aviation::CCallsign &cs, const swift::misc::aviation::CAircraftSituation &situation, const swift::misc::aviation::CAircraftParts &parts)
Test update remote aircraft.
virtual void setModelSetLoaderSimulator(const swift::misc::simulation::CSimulatorInfo &simulator)
Set the model set loader simulator directly.
virtual void recalculateAllAircraft()
Recalculate all aircraft.
virtual swift::misc::simulation::CInterpolationAndRenderingSetupGlobal getInterpolationAndRenderingSetupGlobal() const
The global setup.
virtual bool testRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft, bool add)
Test a remote aircraft.
virtual bool doMatchingAgain(const swift::misc::aviation::CCallsign &callsign)
Repeat the matching callsign.
virtual bool requestElevationBySituation(const swift::misc::aviation::CAircraftSituation &situation)
Request elevation, there is no guarantee the requested elevation will be available in the provider.
virtual void restoreDisabledModels()
Restore the models removed with CAircraftMatcher::disableModelForMatching.
virtual swift::misc::simulation::CSimulatorInfo getModelSetLoaderSimulator() const
Get the model set loader simulator directly.
virtual int doMatchingsAgain()
Repeat all matchings.
virtual swift::misc::simulation::CSimulatorPluginInfoList getAvailableSimulatorPlugins() const
Return list of available simulator plugins.
void gracefulShutdown()
Gracefully shut down, e.g. for plugin unloading.
virtual swift::misc::simulation::CSimulatorPluginInfo getSimulatorPluginInfo() const
Simulator info, currently loaded plugin.
virtual swift::misc::simulation::CSimulatorInfo simulatorsWithInitializedModelSet() const
Simulators which have an initialized model set.
virtual swift::misc::CStatusMessageList getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const
Interpolation messages.
virtual swift::misc::simulation::settings::CSimulatorSettings getSimulatorSettings() const
Get the current simulator settings.
virtual void setMatchingSetup(const swift::misc::simulation::CAircraftMatcherSetup &setup)
Set matching setup.
virtual int checkListeners()
Check all listeners enabled if simulator is connected.
virtual swift::misc::simulation::CMatchingStatistics getCurrentMatchingStatistics(bool missingOnly) const
Current matching statistics.
virtual bool isValidationInProgress() const
Validation in progress.
QPointer< ISimulator > simulator() const
Access to simulator (i.e. the plugin)
virtual swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign getInterpolationAndRenderingSetupPerCallsignOrDefault(const swift::misc::aviation::CCallsign &callsign) const
Get the setup for callsign, if not existing the global setup.
virtual int getModelSetCount() const
Number of installed models in simulator eco system.
virtual swift::misc::simulation::MatchingLog isMatchingMessagesEnabled() const
Enabled mapping logging?
virtual void stopSimulatorPlugin(const swift::misc::simulation::CSimulatorPluginInfo &simulatorInfo)
Stop listener or unload the given plugin (if currently loaded)
virtual bool setInterpolationAndRenderingSetupsPerCallsign(const swift::misc::simulation::CInterpolationSetupList &setups, bool ignoreSameAsGlobal)
Set all setups per callsign.
virtual QStringList getModelSetStrings() const
Model strings.
virtual void enableMatchingMessages(swift::misc::simulation::MatchingLog enabled)
Enable mapping logging.
bool isSimulatorPluginAvailable() const
Simulator plugin available?
virtual swift::misc::simulation::CAircraftModelList getDisabledModelsForMatching() const
The disabled models for matching.
void changedLogOrDebugSettings()
Log or debug values changed.
CCoreFacade * getRuntime()
Runtime.
Definition: context.h:57
bool isUsingImplementingObject() const
Using local implementing object?
Definition: context.h:45
IContextNetwork * getIContextNetwork()
Context for network.
Definition: context.cpp:29
bool isLocalObject() const
Local or remote object?
Definition: context.h:51
const IContextApplication * getIContextApplication() const
Context for application.
Definition: context.cpp:39
IContextOwnAircraft * getIContextOwnAircraft()
Context for own aircraft.
Definition: context.cpp:44
bool isDebugEnabled() const
Debug enabled?
Definition: context.cpp:59
virtual swift::misc::network::CServer getConnectedServer() const =0
Server which is connected, if not connected empty default object.
virtual swift::misc::simulation::CSimulatedAircraft getOwnAircraft() const =0
Get own aircraft.
void renderRestrictionsChanged(bool restricted, bool enabled, int maxAircraft, const swift::misc::physical_quantities::CLength &maxRenderedDistance)
Render restrictions have been changed.
void validatedModelSet(const swift::misc::simulation::CSimulatorInfo &simulator, const swift::misc::simulation::CAircraftModelList &valid, const swift::misc::simulation::CAircraftModelList &invalid, bool stopped, const swift::misc::CStatusMessageList &msgs)
Validated model set.
void addingRemoteModelFailed(const swift::misc::simulation::CSimulatedAircraft &aircraft, bool disabled, bool failover, const swift::misc::CStatusMessage &message)
Adding a remote aircraft failed.
static const QString & InterfaceName()
Service name.
void vitalityLost()
A formerly vital driver is no longer vital/responding.
bool isSimulatorAvailable() const
Simulator avialable (driver available)?
void interpolationAndRenderingSetupChanged()
Setup changed.
void autoPublishDataWritten(const swift::misc::simulation::CSimulatorInfo &simulator)
Auto publish data written for simulator.
void ownAircraftModelChanged(const swift::misc::simulation::CAircraftModel &model)
Emitted when own aircraft model changes.
void simulatorChanged(const swift::misc::simulation::CSimulatorInfo &simulator)
Same as simulatorPluginChanged, only with simulator signature.
void modelSetChanged(const swift::misc::simulation::CSimulatorInfo &simulator)
Model set ready or changed.
void simulatorPluginChanged(const swift::misc::simulation::CSimulatorPluginInfo &info)
Simulator plugin loaded / unloaded (default info)
void modelMatchingCompleted(const swift::misc::simulation::CSimulatedAircraft &aircraft)
A single model has been matched for given aircraft.
void matchingSetupChanged()
Matching setup changed.
void driverMessages(const swift::misc::CStatusMessageList &messages)
Relevant simulator messages to be explicitly displayed.
void simulatorStatusChanged(int status)
Simulator combined status.
void insufficientFrameRateDetected(bool fatal)
Frame rate has fallen too far below the threshold to maintain consistent sim rate.
void simulatorSettingsChanged()
Simulator settings have been changed.
void airspaceSnapshotHandled()
An airspace snapshot was handled.
void aircraftRenderingChanged(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Aircraft rendering changed.
CStatusMessage setAndSave(const T &value, qint64 timestamp=0)
Write and save in the same step. Must be called from the thread in which the owner lives.
Definition: valuecache.h:417
const T & getThreadLocal() const
Read the current value.
Definition: valuecache.h:400
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
size_type size() const
Returns number of elements in the collection.
Definition: collection.h:185
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
void quitAndWait() noexcept final
Calls quit() and blocks until the thread is finished.
Definition: worker.cpp:203
CStatusMessage set(const typename Trait::type &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
Definition: datacache.h:350
Base class with a member CIdentifier to be inherited by a class which has an identity in the environm...
Definition: identifiable.h:24
bool isMyIdentifier(const CIdentifier &otherIdentifier) const
My identifier?
Definition: identifiable.h:33
const CIdentifier & identifier() const
Get identifier.
Definition: identifiable.h:27
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
const QString & getName() const
Name.
Definition: identifier.h:80
static const QString & contextSlot()
Context slots.
Definition: logcategories.h:87
static const QString & cmdLine()
Cmd.line parsing.
void localMessageLogged(const swift::misc::CStatusMessage &message)
Emitted when a message is logged in this process.
static CLogHandler * instance()
Return pointer to the CLogHandler singleton.
void remoteMessageLogged(const swift::misc::CStatusMessage &message)
Emitted when a log message is relayed from a different process.
Class for emitting a log message.
Definition: logmessage.h:27
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Utility methods for simple line parsing used with the command line.
void parse(const QString &commandLine)
Parse.
Streamable status message, e.g.
Status messages, e.g. from Core -> GUI.
static bool thisIsMainThread()
Is the current thread the application thread?
Definition: threadutils.cpp:21
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
Value object encapsulating information of an aircraft's situation.
void setCallsign(const CCallsign &callsign)
Corresponding callsign.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Value object for a set of callsigns.
Definition: callsignset.h:26
Value object for SELCAL.
Definition: selcal.h:31
QString convertToQString(bool i18n=false) const
Cast as QString.
Plane of same elevation, can be a single point or larger area (e.g. airport)
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Value object encapsulating information about a connection status.
bool isConnected() const
Query status.
bool isDisconnected() const
Query status.
QString getServerSessionId(bool onlyConnected) const
Identifying a session, if not connected empty.
Definition: server.cpp:153
Value object encapsulating a list of text messages.
Direct in memory access to client (network client) data.
Physical unit length (length)
Definition: length.h:18
bool doModelAddFailover() const
Failover if model cannot be loaded.
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
const physical_quantities::CLength & getCG() const
Get center of gravity.
void setCG(const physical_quantities::CLength &cg)
Get center of gravity.
Value object encapsulating a list of aircraft models.
QStringList getModelStringList(bool sort=true) const
Model strings.
QStringList toCompleterStrings(bool sorted=true, const CSimulatorInfo &simulator={ CSimulatorInfo::All }) const
Completer strings.
CAircraftModelList findModelsStartingWith(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find models starting with.
int removeModelsWithString(const CAircraftModelList &models, Qt::CaseSensitivity sensitivity)
Remove those models with given model strings.
bool containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model string?
Validate model files from the sets and check if the model still exists.
bool isValidating() const
Validation in progress.
void setCurrentSimulator(const CSimulatorInfo &simulator, const QString &simDirectory, const QStringList &modelDirList)
Corresponding simulator.
bool triggerValidation(const CSimulatorInfo &simulator, const QString &simDirectory)
Trigger a validation, returns false if "work in progress".
void validated(const CSimulatorInfo &simulator, const CAircraftModelList &validModels, const CAircraftModelList &invalidModels, bool stopped, const CStatusMessageList &msgs)
Validated for simulator.
bool setEnabledAircraftParts(bool enabled)
Set enabled aircraft parts.
void setSimulatorDebuggingMessages(bool debug)
Debugging messages for simulation.
Value object for interpolator and rendering per callsign.
Value object for matching statistics.
CMatchingStatistics findMissingOnly() const
Find entires denoting missing entries only.
aviation::CCallsignSet getAircraftInRangeCallsigns() const
Unique callsigns for aircraft in range.
bool isAircraftInRange(const aviation::CCallsign &callsign) const
Is aircraft in range?
void updateMarkAllAsNotRendered()
Mark all as not rendered.
CSimulatedAircraft getAircraftInRangeForCallsign(const aviation::CCallsign &callsign) const
Aircraft for callsign.
void setRemoteAircraftProvider(IRemoteAircraftProvider *remoteAircraftProvider)
Set remote aircraft provider.
bool updateAircraftModel(const aviation::CCallsign &callsign, const CAircraftModel &model, const CIdentifier &originator)
Change model.
Comprehensive information of an aircraft.
bool hasModelString() const
Has model string?
void setModel(const CAircraftModel &model)
Set model.
const simulation::CAircraftModel & getNetworkModel() const
Get network model.
bool hasCallsign() const
Callsign not empty, no further checks.
const aviation::CCallsign & getCallsign() const
Get callsign.
bool resetToNetworkModel()
Reset to the newtork model.
Value object encapsulating a list of aircraft.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool isSingleSimulator() const
Single simulator selected.
bool isNoSimulator() const
No simulator?
static const CSimulatorInfo & p3d()
Const simulator info objects.
Simulator internals for flight simulators. Those are obtained from a running simulator and represent ...
const QString & getIdentifier() const
Identifier.
const CSimulatorInfo & getSimulatorInfo() const
Simulator info object.
bool isUnspecified() const
Unspecified simulator.
bool isValid() const
Check if the provided plugin metadata is valid. Simulator plugin (driver) has to meet the following r...
Value object encapsulating a list of SimulatorInfo objects.
Direct threadsafe in memory access to own aircraft.
Direct thread safe in memory access to remote aircraft.
CStatusMessage setSettings(const CSimulatorSettings &settings, const CSimulatorInfo &simulator)
Set settings per simulator.
QStringList getModelDirectoriesOrDefault(const CSimulatorInfo &simulator) const
Model directory or default model path per simulator.
CSimulatorSettings getSettings(const CSimulatorInfo &simulator) const
Settings per simulator.
QString getSimulatorDirectoryOrDefault(const CSimulatorInfo &simulator) const
Simulator directory or default model path per simulator.
Settings regarding message handling. Driver independent part, related to network.
bool relayThisTextMessage(const network::CTextMessage &msg, const CSimulatedAircraft &aircraft) const
Relay given text message.
bool relayThisStatusMessage(const CStatusMessage &message) const
Relay this particular message.
Settings for simulator Driver independent parts (such as directories), also used in model loaders.
virtual bool parseCommandLine(const QString &commandLine, const swift::misc::CIdentifier &originator)
Parse a given command line.
Classes interacting with the swift database (aka "datastore").
Free functions in swift::misc.
SWIFT_MISC_EXPORT bool stringToBool(const QString &boolString)
Convert string to bool.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Definition: threadutils.h:30
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26