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  m_validator->startUpdating(60);
94  }
95 
96  // For validation we need simulator directory and model directory
97  // this function is called at start (simulator=0) and when there is an active connection to a simulator
98  void CContextSimulator::setValidator(const CSimulatorInfo &simulator)
99  {
100  if (simulator.isSingleSimulator())
101  {
102  const QString simDir = m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator);
103  const QStringList modelDirList = m_multiSimulatorSettings.getModelDirectoriesOrDefault(simulator);
104  m_validator->setCurrentSimulator(simulator, simDir, modelDirList);
105  }
106  else { m_validator->setCurrentSimulator(CSimulatorInfo::None, {}, {}); }
107  }
108 
110  {
111  return m_simulatorPlugin.second && IContextSimulator::isSimulatorAvailable();
112  }
113 
115 
117  {
118  if (m_validator)
119  {
120  disconnect(m_validator);
121  m_validator->quitAndWait();
122  m_validator->deleteLater();
123  m_validator = nullptr;
124  }
125  this->stopSimulatorListeners();
126  this->disconnect();
127  this->unloadSimulatorPlugin();
128 
129  // give listeners a head start
130  // if simconnect is running remotely it can take a while until it shutdowns
131  m_listenersThread.quit();
132  m_listenersThread.wait(10 * 1000);
133  }
134 
136  {
137  if (!m_plugins) { return {}; }
138  return m_plugins->getAvailableSimulatorPlugins();
139  }
140 
142  {
144  if (!p.isValid()) { return {}; }
145  const CSimulatorInfo sim = p.getSimulatorInfo();
146  if (!sim.isSingleSimulator()) { return {}; }
147  return m_multiSimulatorSettings.getSettings(sim);
148  }
149 
151  {
152  if (!simulator.isSingleSimulator()) { return false; }
153  const CSimulatorSettings simSettings = m_multiSimulatorSettings.getSettings(simulator);
154  if (simSettings == settings) { return false; }
155  const CStatusMessage msg = m_multiSimulatorSettings.setSettings(settings, simulator);
157  emit this->simulatorSettingsChanged();
158  return true;
159  }
160 
162  {
163  return this->listenForSimulator(simulatorInfo);
164  }
165 
167  {
168  if (!m_simulatorPlugin.first.isUnspecified() && m_simulatorPlugin.first == simulatorInfo)
169  {
170  this->unloadSimulatorPlugin();
171  }
172 
173  ISimulatorListener *listener = m_plugins->getListener(simulatorInfo.getIdentifier());
174  Q_ASSERT(listener);
175  QMetaObject::invokeMethod(listener, &ISimulatorListener::stop, Qt::QueuedConnection);
176  }
177 
179  {
180  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
181  if (!m_plugins) { return 0; }
182  return m_plugins->checkAvailableListeners();
183  }
184 
185  ISimulator::SimulatorStatus CContextSimulator::getSimulatorStatus() const
186  {
187  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
188  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return ISimulator::Unspecified; }
189  return m_simulatorPlugin.second->getSimulatorStatus();
190  }
191 
193  {
194  static const CSimulatorPluginInfo unspecified;
195  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
196  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return unspecified; }
197  if (m_simulatorPlugin.first.getSimulator().contains("emulated", Qt::CaseInsensitive))
198  {
199  return m_simulatorPlugin.second->getSimulatorPluginInfo();
200  }
201  return m_simulatorPlugin.first;
202  }
203 
205  {
206  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
207  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CSimulatorInternals(); }
208  return m_simulatorPlugin.second->getSimulatorInternals();
209  }
210 
212  {
213  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
215  if (!simulator.isSingleSimulator()) { return CAircraftModelList(); }
216 
217  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
218  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
219  }
220 
222  {
223  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
224  if (m_simulatorPlugin.second)
225  {
226  if (m_simulatorPlugin.second->isConnected())
227  {
228  if (m_simulatorPlugin.second->isEmulatedDriver())
229  {
230  // specialized version returning the "emulated info"
231  return this->getSimulatorPluginInfo().getSimulatorInfo();
232  }
233  return m_simulatorPlugin.first.getSimulatorInfo();
234  }
235  }
236  return m_modelSetSimulator.get();
237  }
238 
240  {
241  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
242  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
243  if (this->isSimulatorAvailable()) { return; } // if a plugin is loaded, do ignore this
244  m_modelSetSimulator.set(simulator);
245  const CAircraftModelList models = this->getModelSet(); // cache synced
246  m_aircraftMatcher.setModelSet(models, simulator, false);
247  }
248 
250  {
251  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
252  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().simulatorsWithInitializedCache();
253  }
254 
256  {
257  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
258  CStatusMessageList msgs;
259  const CSimulatorInfo simulators = this->simulatorsWithInitializedModelSet();
260  if (simulators.isNoSimulator())
261  {
262  msgs.push_back(CStatusMessage(this).validationError(
263  u"No model set so far, you need at least one model set. Hint: You can create a model set in the "
264  u"mapping tool, or copy an existing set in the launcher."));
265  }
266  return msgs;
267  }
268 
270  {
271  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
272  return this->getModelSet().getModelStringList(false);
273  }
274 
275  bool CContextSimulator::isKnownModelInSet(const QString &modelString) const
276  {
277  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
278  const bool known = this->getModelSet().containsModelString(modelString);
279  return known;
280  }
281 
283  {
284  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
285  if (removeModels.isEmpty()) { return 0; }
286  const CSimulatorInfo simulator = m_modelSetSimulator.get();
287  if (!simulator.isSingleSimulator()) { return 0; }
288 
289  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
290  CAircraftModelList models =
291  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
292  const int removed = models.removeModelsWithString(removeModels, Qt::CaseInsensitive);
293  if (removed > 0)
294  {
295  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().setCachedModels(models, simulator);
296  }
297  return removed;
298  }
299 
300  QStringList CContextSimulator::getModelSetCompleterStrings(bool sorted) const
301  {
302  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << sorted; }
303  return this->getModelSet().toCompleterStrings(sorted);
304  }
305 
307  {
308  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
309  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return 0; }
310  return this->getModelSet().size();
311  }
312 
313  void CContextSimulator::disableModelsForMatching(const CAircraftModelList &removedModels, bool incremental)
314  {
315  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
316  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
317  m_aircraftMatcher.disableModelsForMatching(removedModels, incremental);
318  }
319 
321  {
322  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
323  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CAircraftModelList(); }
324  return m_aircraftMatcher.getDisabledModelsForMatching();
325  }
326 
328  {
329  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
330  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
331  m_aircraftMatcher.restoreDisabledModels();
332  }
333 
335  {
336  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
337  if (!m_validator) { return false; }
338  return m_validator->isValidating();
339  }
340 
342  {
343  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
344  if (!m_validator) { return false; }
345  const QString simDir =
346  simulator.isSingleSimulator() ? m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator) : "";
347  return m_validator->triggerValidation(simulator, simDir);
348  }
349 
351  {
352  if (isDebugEnabled())
353  {
354  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << modelString;
355  }
356  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CAircraftModelList(); }
357 
358  return this->getModelSet().findModelsStartingWith(modelString);
359  }
360 
362  {
363  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
364  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return m_renderSettings.get(); }
365  return m_simulatorPlugin.second->getInterpolationSetupGlobal();
366  }
367 
369  {
370  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
371  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CInterpolationSetupList(); }
372  return m_simulatorPlugin.second->getInterpolationSetupsPerCallsign();
373  }
374 
377  {
378  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
379  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
380  {
382  }
383  return m_simulatorPlugin.second->getInterpolationSetupPerCallsignOrDefault(callsign);
384  }
385 
386  bool CContextSimulator::setInterpolationAndRenderingSetupsPerCallsign(const CInterpolationSetupList &setups,
387  bool ignoreSameAsGlobal)
388  {
389  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
390  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return false; }
391  return m_simulatorPlugin.second->setInterpolationSetupsPerCallsign(setups, ignoreSameAsGlobal);
392  }
393 
395  {
396  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup; }
397 
398  // anyway save for future reference
399  const CStatusMessage m = m_renderSettings.setAndSave(setup);
401 
402  // transfer to sim
403  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
404  m_simulatorPlugin.second->setInterpolationSetupGlobal(setup);
405  }
406 
408  {
409  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
410  if (callsign.isEmpty()) { return CStatusMessageList(); }
411  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return CStatusMessageList(); }
412  return m_simulatorPlugin.second->getInterpolationMessages(callsign);
413  }
414 
415  bool CContextSimulator::loadSimulatorPlugin(const CSimulatorPluginInfo &simulatorPluginInfo)
416  {
417  Q_ASSERT(this->getIContextApplication());
419  Q_ASSERT(!simulatorPluginInfo.isUnspecified());
420  Q_ASSERT(CThreadUtils::thisIsMainThread()); // only run in main thread
421 
422  // Is a plugin already loaded?
423  if (!m_simulatorPlugin.first.isUnspecified())
424  {
425  // This can happen, if a listener emitted simulatorStarted twice or two different simulators
426  // are running at the same time. In this case, we leave the loaded plugin and just return.
427  return false;
428  }
429 
430  if (!simulatorPluginInfo.isValid())
431  {
432  CLogMessage(this).error(u"Illegal plugin");
433  return false;
434  }
435 
436  ISimulatorFactory *factory = m_plugins->getFactory(simulatorPluginInfo.getIdentifier());
437  Q_ASSERT_X(factory, Q_FUNC_INFO, "no factory");
438 
439  // We assume we run in the same process as the own aircraft context
440  // Hence we pass in memory reference to own aircraft object
441  Q_ASSERT_X(this->getIContextOwnAircraft()->isUsingImplementingObject(), Q_FUNC_INFO,
442  "Need implementing object");
443  Q_ASSERT_X(this->getIContextNetwork()->isUsingImplementingObject(), Q_FUNC_INFO, "Need implementing object");
444  IOwnAircraftProvider *ownAircraftProvider = this->getRuntime()->getCContextOwnAircraft();
445  IRemoteAircraftProvider *renderedAircraftProvider = this->getRuntime()->getCContextNetwork();
446  IClientProvider *clientProvider = this->getRuntime()->getCContextNetwork();
447  ISimulator *simulator =
448  factory->create(simulatorPluginInfo, ownAircraftProvider, renderedAircraftProvider, clientProvider);
449  Q_ASSERT_X(simulator, Q_FUNC_INFO, "no simulator driver can be created");
450 
451  this->setRemoteAircraftProvider(renderedAircraftProvider);
452 
453  // use simulator info from ISimulator as it can access the emulated driver settings
454  CSimulatorInfo simInfo = simulator->getSimulatorInfo();
455  if (!simInfo.isSingleSimulator())
456  {
457  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Invalid simulator from plugin");
458  simInfo = CSimulatorInfo::p3d();
459  if (simulator->isEmulatedDriver())
460  {
461  CLogMessage(this).error(
462  u"Emulated driver does NOT provide valid simulator, using default '%1', plugin: '%2'")
463  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
464  }
465  else
466  {
467  CLogMessage(this).error(u"Invalid driver using default '%1', plugin: '%2'")
468  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
469  }
470  }
471  Q_ASSERT_X(simInfo.isSingleSimulator(), Q_FUNC_INFO, "need single simulator");
472 
473  m_modelSetSimulator.set(simInfo);
474  const CAircraftModelList modelSetModels = this->getModelSet(); // synced
475  m_aircraftMatcher.setModelSet(modelSetModels, simInfo, true);
476  m_aircraftMatcher.setDefaultModel(simulator->getDefaultModel());
477 
478  bool c =
479  connect(simulator, &ISimulator::simulatorStatusChanged, this, &CContextSimulator::onSimulatorStatusChanged);
480  Q_ASSERT(c);
482  &CContextSimulator::onAddingRemoteAircraftFailed);
483  Q_ASSERT(c);
485  &CContextSimulator::onOwnSimulatorModelChanged);
486  Q_ASSERT(c);
489  Q_ASSERT(c);
492  Q_ASSERT(c);
495  Q_ASSERT(c);
497  Q_ASSERT(c);
499  Q_ASSERT(c);
501  Q_ASSERT(c);
502 
503  // disconnect for X-Plane FPS below 20
504  c = connect(simulator, &ISimulator::insufficientFrameRateDetected, this, [this](bool fatal) {
505  if (fatal) { emit this->vitalityLost(); }
506  });
507  Q_ASSERT(c);
510  Q_ASSERT(c);
511 
512  // log from context to simulator
514  &CContextSimulator::relayStatusMessageToSimulator);
515  Q_ASSERT(c);
517  &CContextSimulator::relayStatusMessageToSimulator);
518  Q_ASSERT(c);
519  Q_UNUSED(c)
520 
521  // Once the simulator signaled it is ready to simulate, add all known aircraft
522  m_initallyAddAircraft = true;
523  m_wasSimulating = false;
524  m_matchingMessages.clear();
525  m_failoverAddingCounts.clear();
526 
527  // try to connect to simulator
528  const bool connected = simulator->connectTo();
529  if (!connected)
530  {
531  CLogMessage(this).error(u"Simulator plugin connection to simulator '%1' failed")
532  << simulatorPluginInfo.toQString(true);
533  return false;
534  }
535 
536  // when everything is set up connected, update the current plugin info
537  m_simulatorPlugin.first = simulatorPluginInfo;
538  m_simulatorPlugin.second = simulator;
539  m_simulatorPlugin.second->setInterpolationSetupGlobal(m_renderSettings.get());
540 
541  // Emit signal after this function completes completely decoupled
542  QPointer<CContextSimulator> myself(this);
543  QTimer::singleShot(25, this, [=] {
544  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
545  if (m_simulatorPlugin.second)
546  {
547  CLogMessage(this).info(u"Simulator plugin loaded: '%1' connected: %2")
548  << simulatorPluginInfo.toQString(true) << boolToYesNo(connected);
549 
550  // use the real driver as this will also work eith emulated driver
551  emit this->simulatorPluginChanged(m_simulatorPlugin.second->getSimulatorPluginInfo());
552  emit this->simulatorChanged(m_simulatorPlugin.second->getSimulatorInfo());
553  }
554  });
555 
556  return true;
557  }
558 
559  bool CContextSimulator::listenForSimulator(const CSimulatorPluginInfo &simulatorInfo)
560  {
561  Q_ASSERT(this->getIContextApplication());
563  Q_ASSERT(!simulatorInfo.isUnspecified());
564  Q_ASSERT(m_simulatorPlugin.first.isUnspecified());
565 
566  if (!m_listenersThread.isRunning())
567  {
568  m_listenersThread.setObjectName("CContextSimulator: Thread for listener " + simulatorInfo.getIdentifier());
569  m_listenersThread.start(QThread::LowPriority);
570  }
571 
572  ISimulatorListener *listener = m_plugins->createListener(simulatorInfo.getIdentifier());
573  if (!listener) { return false; }
574 
575  if (listener->thread() != &m_listenersThread)
576  {
577  Q_ASSERT_X(!listener->parent(), Q_FUNC_INFO, "Objects with parent cannot be moved to thread");
578 
579  const bool c = connect(listener, &ISimulatorListener::simulatorStarted, this,
580  &CContextSimulator::onSimulatorStarted, Qt::QueuedConnection);
581  if (!c)
582  {
583  CLogMessage(this).error(u"Unable to use '%1'") << simulatorInfo.toQString();
584  return false;
585  }
586  listener->setProperty("isInitialized", true);
587  listener->moveToThread(&m_listenersThread);
588  }
589 
590  // start if not already running
591  if (!listener->isRunning())
592  {
593  const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::start, Qt::QueuedConnection);
594  Q_ASSERT_X(s, Q_FUNC_INFO, "cannot invoke method");
595  Q_UNUSED(s)
596  }
597  CLogMessage(this).info(u"Listening for simulator '%1'") << simulatorInfo.getIdentifier();
598  return true;
599  }
600 
601  void CContextSimulator::listenForAllSimulators()
602  {
603  const auto plugins = getAvailableSimulatorPlugins();
604  for (const CSimulatorPluginInfo &p : plugins)
605  {
606  Q_ASSERT(!p.isUnspecified());
607  if (p.isValid()) { this->listenForSimulator(p); }
608  }
609  }
610 
611  void CContextSimulator::unloadSimulatorPlugin()
612  {
613  if (!m_simulatorPlugin.first.isUnspecified())
614  {
615  ISimulator *simulator = m_simulatorPlugin.second;
616  if (simulator && simulator->isConnected())
617  {
618  // we are about to unload an connected simulator
619  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
621  }
622 
623  m_simulatorPlugin.second = nullptr;
624  m_simulatorPlugin.first = CSimulatorPluginInfo();
625 
626  Q_ASSERT(this->getIContextNetwork());
627  Q_ASSERT(this->getIContextNetwork()->isLocalObject());
628 
629  // unload and disconnect
630  if (simulator)
631  {
632  // disconnect signals and delete
633  simulator->disconnect(this);
634  simulator->unload();
635  simulator->deleteLater();
637  emit this->simulatorChanged(CSimulatorInfo());
638  }
639 
640  if (m_wasSimulating) { emit this->vitalityLost(); }
641  m_wasSimulating = false;
642  }
643  }
644 
645  void CContextSimulator::xCtxAddedRemoteAircraftReadyForModelMatching(const CSimulatedAircraft &remoteAircraft)
646  {
647  if (!this->isSimulatorPluginAvailable()) { return; }
648 
649  const CCallsign callsign = remoteAircraft.getCallsign();
650  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Remote aircraft with empty callsign");
651  if (callsign.isEmpty()) { return; }
652 
653  // here we find the best simulator model for a resolved model
654  // in the first step we already tried to find accurate ICAO codes etc.
655  // coming from CAirspaceMonitor::sendReadyForModelMatching
656  MatchingLog whatToLog = m_logMatchingMessages;
657  CStatusMessageList matchingMessages;
658  CStatusMessageList *pMatchingMessages = m_logMatchingMessages > 0 ? &matchingMessages : nullptr;
659  CAircraftModel aircraftModel =
660  m_aircraftMatcher.getClosestMatch(remoteAircraft, whatToLog, pMatchingMessages, true);
661  Q_ASSERT_X(remoteAircraft.getCallsign() == aircraftModel.getCallsign(), Q_FUNC_INFO, "Mismatching callsigns");
662 
663  // decide CG
664  const CLength cgModel = aircraftModel.getCG();
665  const CLength cgSim = m_simulatorPlugin.second->getSimulatorCGPerModelString(aircraftModel.getModelString());
666  const CSimulatorSettings simSettings = this->getSimulatorSettings();
667  switch (simSettings.getCGSource())
668  {
669  case CSimulatorSettings::CGFromSimulatorOnly: aircraftModel.setCG(cgSim); break;
670  case CSimulatorSettings::CGFromSimulatorFirst:
671  if (!cgSim.isNull()) { aircraftModel.setCG(cgSim); }
672  break;
673  case CSimulatorSettings::CGFromDBFirst:
674  if (cgModel.isNull()) { aircraftModel.setCG(cgSim); }
675  break;
676  // case CSimulatorSettings::CGFromDBOnly:
677  default: break; // leave CG from model alone
678  }
679 
680  const CLength overriddenCG =
681  m_simulatorPlugin.second->overriddenCGorDefault(CLength::null(), aircraftModel.getModelString());
682  if (!overriddenCG.isNull()) { aircraftModel.setCG(overriddenCG); }
683 
684  // model in provider
685  this->updateAircraftModel(callsign, aircraftModel, this->identifier());
686 
687  const CSimulatedAircraft aircraftAfterModelApplied =
688  this->getAircraftInRangeForCallsign(remoteAircraft.getCallsign());
689  if (!aircraftAfterModelApplied.hasModelString())
690  {
691  if (!aircraftAfterModelApplied.hasCallsign()) { return; } // removed
692  if (this->isAircraftInRange(aircraftAfterModelApplied.getCallsign()))
693  {
694  return;
695  } // removed, but callsig, we did crosscheck
696 
697  // callsign, but no model string
698  CLogMessage(this).error(u"Matching error for '%1', no model string")
699  << aircraftAfterModelApplied.getCallsign().asString();
700 
701  CSimulatedAircraft brokenAircraft(aircraftAfterModelApplied);
702  brokenAircraft.setEnabled(false);
703  brokenAircraft.setRendered(false);
704  CCallsign::addLogDetailsToList(
705  pMatchingMessages, callsign,
706  QStringLiteral("Cannot add remote aircraft, no model string: '%1'").arg(brokenAircraft.toQString()));
707  emit this->aircraftRenderingChanged(brokenAircraft);
708  return;
709  }
710 
711  // here the model is added to the simulator
712  m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraftAfterModelApplied);
713  CCallsign::addLogDetailsToList(
714  pMatchingMessages, callsign,
715  QStringLiteral("Logically added remote aircraft: %1").arg(aircraftAfterModelApplied.toQString()));
716 
717  this->clearMatchingMessages(callsign);
718  this->addMatchingMessages(callsign, matchingMessages);
719 
720  // done
721  emit this->modelMatchingCompleted(aircraftAfterModelApplied);
722  }
723 
724  void CContextSimulator::xCtxRemovedRemoteAircraft(const CCallsign &callsign)
725  {
726  if (!this->isSimulatorAvailable()) { return; }
727  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
728  m_failoverAddingCounts.remove(callsign);
729  }
730 
731  void CContextSimulator::onSimulatorStatusChanged(ISimulator::SimulatorStatus status)
732  {
733  m_wasSimulating = m_wasSimulating || status.testFlag(ISimulator::Simulating);
734  if (m_initallyAddAircraft && status.testFlag(ISimulator::Simulating))
735  {
736  // use network to initally add aircraft
737  IContextNetwork *networkContext = this->getIContextNetwork();
738  Q_ASSERT_X(networkContext, Q_FUNC_INFO, "Need context");
739  Q_ASSERT_X(networkContext->isLocalObject(), Q_FUNC_INFO, "Need local object");
740 
741  // initially add aircraft
742  const CSimulatedAircraftList aircraft = networkContext->getAircraftInRange();
743  for (const CSimulatedAircraft &simulatedAircraft : aircraft)
744  {
745  SWIFT_VERIFY_X(!simulatedAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "Need callsign");
746  this->xCtxAddedRemoteAircraftReadyForModelMatching(simulatedAircraft);
747  }
748  m_initallyAddAircraft = false;
749  }
750 
751  if (!status.testFlag(ISimulator::Connected))
752  {
753  // we got disconnected, plugin no longer needed
754  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
755  this->unloadSimulatorPlugin();
756  this->restoreSimulatorPlugins();
757 
758  if (m_wasSimulating) { emit this->vitalityLost(); }
759  m_wasSimulating = false;
760  }
761 
762  emit this->simulatorStatusChanged(status);
763  }
764 
765  void CContextSimulator::xCtxTextMessagesReceived(const network::CTextMessageList &textMessages)
766  {
767  if (!this->isSimulatorAvailable()) { return; }
768  if (!this->getIContextOwnAircraft()) { return; }
769 
770  const CSimulatorMessagesSettings settings = m_messageSettings.getThreadLocal();
771  const CSimulatedAircraft ownAircraft = this->getIContextOwnAircraft()->getOwnAircraft();
772  for (const auto &tm : textMessages)
773  {
774  if (!settings.relayThisTextMessage(tm, ownAircraft)) { continue; }
775  m_simulatorPlugin.second->displayTextMessage(tm);
776  }
777  }
778 
779  void CContextSimulator::xCtxChangedRemoteAircraftModel(const CSimulatedAircraft &aircraft,
780  const swift::misc::CIdentifier &originator)
781  {
782  if (CIdentifiable::isMyIdentifier(originator)) { return; }
783  if (!this->isSimulatorAvailable()) { return; }
784  m_simulatorPlugin.second->changeRemoteAircraftModel(aircraft);
785  }
786 
787  void CContextSimulator::xCtxChangedOwnAircraftModel(const CAircraftModel &aircraftModel,
788  const CIdentifier &originator)
789  {
790  if (CIdentifiable::isMyIdentifier(originator)) { return; }
791  if (!this->isSimulatorAvailable()) { return; }
792 
793  emit this->ownAircraftModelChanged(aircraftModel);
794  }
795 
796  void CContextSimulator::xCtxChangedRemoteAircraftEnabled(const CSimulatedAircraft &aircraft)
797  {
798  if (!this->isSimulatorAvailable()) { return; }
799  m_simulatorPlugin.second->changeRemoteAircraftEnabled(aircraft);
800  }
801 
802  void CContextSimulator::xCtxNetworkConnectionStatusChanged(const CConnectionStatus &from,
803  const CConnectionStatus &to)
804  {
805  Q_UNUSED(from)
806  SWIFT_VERIFY_X(this->getIContextNetwork(), Q_FUNC_INFO, "Missing network context");
807  if (to.isConnected() && this->getIContextNetwork())
808  {
809  m_networkSessionId = this->getIContextNetwork()->getConnectedServer().getServerSessionId(false);
810  if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
811  {
812  m_simulatorPlugin.second->setFlightNetworkConnected(true);
813  }
814  }
815  else if (to.isDisconnected())
816  {
817  m_networkSessionId.clear();
818  m_aircraftMatcher.clearMatchingStatistics();
819  m_matchingMessages.clear();
820  m_failoverAddingCounts.clear();
821 
822  if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
823  {
824  m_simulatorPlugin.second->removeAllRemoteAircraft(); // also removes aircraft
825  m_simulatorPlugin.second->setFlightNetworkConnected(false);
826  }
827  }
828  }
829 
830  void CContextSimulator::onAddingRemoteAircraftFailed(const CSimulatedAircraft &remoteAircraft, bool disabled,
831  bool requestFailover, const CStatusMessage &message)
832  {
833  if (!this->isSimulatorAvailable()) { return; }
834  if (disabled) { m_aircraftMatcher.addingRemoteModelFailed(remoteAircraft); }
835 
836  const CCallsign cs = remoteAircraft.getCallsign();
837  if (cs.isEmpty()) { return; }
838 
839  bool failover = requestFailover;
840  if (requestFailover)
841  {
842  const CAircraftMatcherSetup setup = m_aircraftMatcher.getSetup();
843  if (setup.doModelAddFailover())
844  {
845  const int trial = m_failoverAddingCounts.value(cs, 0);
846  if (trial < MaxModelAddedFailoverTrials)
847  {
848  m_failoverAddingCounts[cs] = trial + 1;
849  this->doMatchingAgain(cs); // has its own single shot logic
850  }
851  else
852  {
853  failover = false;
854  CLogMessage(this).warning(u"Model for '%1' failed adding, tried %2 time(s) to resolve, giving up")
855  << cs.toQString(true) << (trial + 1);
856  }
857  }
858  }
859  emit this->addingRemoteModelFailed(remoteAircraft, disabled, failover, message);
860  }
861 
862  void CContextSimulator::xCtxUpdateSimulatorCockpitFromContext(const CSimulatedAircraft &ownAircraft,
863  const CIdentifier &originator)
864  {
865  if (!this->isSimulatorAvailable()) { return; }
866  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
867 
868  // update
869  m_simulatorPlugin.second->updateOwnSimulatorCockpit(ownAircraft, originator);
870  }
871 
872  void CContextSimulator::xCtxUpdateSimulatorSelcalFromContext(const CSelcal &selcal, const CIdentifier &originator)
873  {
874  if (!this->isSimulatorAvailable()) { return; }
875  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
876 
877  // update
878  m_simulatorPlugin.second->updateOwnSimulatorSelcal(selcal, originator);
879  }
880 
881  void CContextSimulator::xCtxNetworkRequestedNewAircraft(const CCallsign &callsign, const QString &aircraftIcao,
882  const QString &airlineIcao, const QString &livery)
883  {
884  if (m_networkSessionId.isEmpty()) { return; }
885  m_aircraftMatcher.evaluateStatisticsEntry(m_networkSessionId, callsign, aircraftIcao, airlineIcao, livery);
886  }
887 
888  void CContextSimulator::relayStatusMessageToSimulator(const swift::misc::CStatusMessage &message)
889  {
890  if (!this->isSimulatorAvailable()) { return; }
891  if (!sApp || sApp->isShuttingDown()) { return; }
892  const CSimulatorMessagesSettings simMsg = m_messageSettings.getThreadLocal();
893  if (simMsg.relayThisStatusMessage(message) && m_simulatorPlugin.second)
894  {
895  m_simulatorPlugin.second->displayStatusMessage(message);
896  }
897  }
898 
899  void CContextSimulator::changeEnabledSimulators()
900  {
901  CSimulatorPluginInfo currentPluginInfo = m_simulatorPlugin.first;
902  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
903 
904  // Unload the current plugin, if it is no longer enabled
905  if (!currentPluginInfo.isUnspecified() && !enabledSimulators.contains(currentPluginInfo.getIdentifier()))
906  {
907  unloadSimulatorPlugin();
909  }
910  restoreSimulatorPlugins();
911  }
912 
913  void CContextSimulator::restoreSimulatorPlugins()
914  {
915  if (!m_simulatorPlugin.first.isUnspecified()) { return; }
916 
917  this->stopSimulatorListeners();
918  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
919  const CSimulatorPluginInfoList allSimulators = m_plugins->getAvailableSimulatorPlugins();
920  for (const CSimulatorPluginInfo &s : allSimulators)
921  {
922  if (enabledSimulators.contains(s.getIdentifier())) { startSimulatorPlugin(s); }
923  }
924  }
925 
927  {
928  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
929  return m_matchingMessages[callsign];
930  }
931 
933  {
934  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
935  return m_logMatchingMessages;
936  }
937 
939  {
940  if (isDebugEnabled())
941  {
942  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << matchingLogToString(enabled);
943  }
944  if (m_logMatchingMessages == enabled) { return; }
945  m_logMatchingMessages = enabled;
947  }
948 
950  {
951  if (isDebugEnabled())
952  {
953  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << missingOnly;
954  }
955  const CMatchingStatistics statistics = m_aircraftMatcher.getCurrentStatistics();
956  return missingOnly ? statistics.findMissingOnly() : statistics;
957  }
958 
960  {
961  if (isDebugEnabled())
962  {
963  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup.toQString();
964  }
965  m_aircraftMatcher.setSetup(setup);
966  const CStatusMessage msg = m_matchingSettings.setAndSave(setup);
968  }
969 
971  {
972  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
973  return m_aircraftMatcher.getSetup();
974  }
975 
977  {
978  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
979  bool added = add;
980  if (add)
981  {
982  m_simulatorPlugin.second->setTestMode(true);
983  added = m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraft);
984  }
985  else
986  {
987  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(aircraft.getCallsign());
988  m_simulatorPlugin.second->setTestMode(false); // AFTER we have removed it
989  }
990  return added;
991  }
992 
994  const CAircraftParts &parts)
995  {
996  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
997  CAircraftSituation s = situation; // make sure to have correct callsign
998  s.setCallsign(cs);
999  return m_simulatorPlugin.second->testSendSituationAndParts(cs, s, parts);
1000  }
1001 
1002  bool CContextSimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
1003  {
1004  Q_UNUSED(originator)
1005  if (commandLine.isEmpty()) { return false; }
1006  CSimpleCommandParser parser({
1007  ".plugin", ".drv", ".driver", // forwarded to driver
1008  ".ris" // rendering interpolator setup
1009  });
1010  parser.parse(commandLine);
1011  if (!parser.isKnownCommand()) { return false; }
1012  if (parser.matchesCommand("ris"))
1013  {
1015  const QString p1 = parser.part(1);
1016  if (!parser.hasPart(2)) { return false; }
1017  const bool on = stringToBool(parser.part(2));
1018  if (p1 == "debug") { rs.setSimulatorDebuggingMessages(on); }
1019  else if (p1 == "parts") { rs.setEnabledAircraftParts(on); }
1020  else { return false; }
1022  CLogMessage(this, CLogCategories::cmdLine()).info(u"Setup is: '%1'") << rs.toQString(true);
1023  return true;
1024  }
1025  if (parser.matchesCommand("plugin") || parser.matchesCommand("drv") || parser.matchesCommand("driver"))
1026  {
1027  if (!m_simulatorPlugin.second) { return false; }
1028  return m_simulatorPlugin.second->parseCommandLine(commandLine, originator);
1029  }
1030  return false;
1031  }
1032 
1033  QPointer<ISimulator> CContextSimulator::simulator() const
1034  {
1035  if (!this->isSimulatorAvailable() || !m_simulatorPlugin.second) { return nullptr; }
1036  return m_simulatorPlugin.second;
1037  }
1038 
1040  {
1041  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
1042  if (!m_simulatorPlugin.second) { return false; }
1043  return m_simulatorPlugin.second->followAircraft(callsign);
1044  }
1045 
1047  {
1048  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1049  if (!m_simulatorPlugin.second) { return; }
1050  m_simulatorPlugin.second->recalculateAllAircraft();
1051  }
1052 
1054  {
1055  CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1056  if (aircraft.getCallsign() != callsign) { return false; } // not found
1057  if (!this->isSimulatorAvailable()) { return false; }
1058 
1059  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
1060  aircraft.setModel(aircraft.getNetworkModel()); // like originally from network
1061  QPointer<CContextSimulator> myself(this);
1062  QTimer::singleShot(1000, this, [=] {
1063  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1064  this->xCtxAddedRemoteAircraftReadyForModelMatching(aircraft);
1065  });
1066  return true;
1067  }
1068 
1070  {
1071  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << situation; }
1072  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
1073  return m_simulatorPlugin.second->requestElevationBySituation(situation);
1074  }
1075 
1077  const CLength &range) const
1078  {
1079  if (isDebugEnabled())
1080  {
1082  << Q_FUNC_INFO << reference.convertToQString(true) << range;
1083  }
1084  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return CElevationPlane::null(); }
1085  return m_simulatorPlugin.second->findClosestElevationWithinRange(reference, range);
1086  }
1087 
1089  {
1090  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1091  const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
1092  if (callsigns.isEmpty()) { return 0; }
1093  int delayMs = 25;
1094  QPointer<CContextSimulator> myself(this);
1095  for (const CCallsign &cs : callsigns)
1096  {
1097  QTimer::singleShot(delayMs, this, [=] {
1098  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1099  this->doMatchingAgain(cs);
1100  });
1101  delayMs += 25;
1102  }
1103  return callsigns.size();
1104  }
1105 
1107  {
1108  if (isDebugEnabled())
1109  {
1110  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign.asString();
1111  }
1112  if (!this->isAircraftInRange(callsign)) { return false; }
1113  if (!this->isSimulatorAvailable()) { return false; }
1114 
1115  QPointer<CContextSimulator> myself(this);
1116  QTimer::singleShot(2500, this, [=] {
1117  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1118  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1119  if (!aircraft.hasCallsign()) { return; } // no longer valid
1120  CSimulatedAircraft resetAircraft(aircraft);
1121  resetAircraft.resetToNetworkModel();
1122  resetAircraft.setEnabled(true);
1123  this->xCtxAddedRemoteAircraftReadyForModelMatching(resetAircraft);
1124  });
1125  return true;
1126  }
1127 
1128  void CContextSimulator::onSimulatorStarted(const CSimulatorPluginInfo &info)
1129  {
1130  this->stopSimulatorListeners();
1131  this->loadSimulatorPlugin(info);
1132 
1133  // if we have enabled messages, we will disable if size getting too high
1134  if (m_logMatchingMessages)
1135  {
1136  const QPointer<CContextSimulator> myself(this);
1137  QTimer::singleShot(5000, this, [=] {
1138  if (!myself) { return; }
1139  if (m_aircraftMatcher.getModelSetCount() > MatchingLogMaxModelSetSize)
1140  {
1141  const MatchingLog log = CBuildConfig::isDebugBuild() ? MatchingLogAll : MatchingLogSimplified;
1142  this->enableMatchingMessages(log);
1143  }
1144  });
1145  }
1146  }
1147 
1148  void CContextSimulator::onOwnSimulatorModelChanged(const CAircraftModel &model)
1149  {
1150  CAircraftModel lookupModel(model);
1151  if (!lookupModel.isLoadedFromDb()) { lookupModel = this->reverseLookupModel(model); }
1152  emit this->ownAircraftModelChanged(lookupModel);
1153  }
1154 
1155  void CContextSimulator::stopSimulatorListeners()
1156  {
1158  {
1159  ISimulatorListener *listener = m_plugins->getListener(info.getIdentifier());
1160  if (listener)
1161  {
1162  const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::stop);
1163  Q_ASSERT_X(s, Q_FUNC_INFO, "Cannot invoke stop");
1164  Q_UNUSED(s)
1165  }
1166  }
1167  }
1168 
1169  void CContextSimulator::addMatchingMessages(const CCallsign &callsign, const CStatusMessageList &messages)
1170  {
1171  if (callsign.isEmpty()) { return; }
1172  if (messages.isEmpty()) { return; }
1173  if (!m_logMatchingMessages) { return; }
1174  if (m_matchingMessages.contains(callsign))
1175  {
1176  CStatusMessageList &msgs = m_matchingMessages[callsign];
1177  msgs.push_back(messages);
1178  }
1179  else { m_matchingMessages.insert(callsign, messages); }
1180  }
1181 
1182  void CContextSimulator::clearMatchingMessages(const CCallsign &callsign)
1183  {
1184  if (callsign.isEmpty()) { return; }
1185  m_matchingMessages.remove(callsign);
1186  }
1187 
1188  CAircraftModel CContextSimulator::reverseLookupModel(const CAircraftModel &model)
1189  {
1190  bool modified = false;
1191  const CAircraftModel reverseModel =
1192  CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified);
1193  return reverseModel;
1194  }
1195 
1196  void CContextSimulator::initByLastUsedModelSet()
1197  {
1198  // no models in matcher, but in cache, we can set them as default
1199  const CSimulatorInfo simulator(m_modelSetSimulator.get());
1200  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
1201  const CAircraftModelList models(this->getModelSet()); // synced
1202  CLogMessage(this).info(u"Init aircraft matcher with %1 models from set for '%2'")
1203  << models.size() << simulator.toQString();
1204  m_aircraftMatcher.setModelSet(models, simulator, false);
1205  }
1206 } // 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:205
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:76
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