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 
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);
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);
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 {}; }
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 {}; }
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 
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 {}; }
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 {}; }
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 {}; }
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()) { return {}; }
381  return m_simulatorPlugin.second->getInterpolationSetupPerCallsignOrDefault(callsign);
382  }
383 
384  bool CContextSimulator::setInterpolationAndRenderingSetupsPerCallsign(const CInterpolationSetupList &setups,
385  bool ignoreSameAsGlobal)
386  {
387  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
388  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return false; }
389  return m_simulatorPlugin.second->setInterpolationSetupsPerCallsign(setups, ignoreSameAsGlobal);
390  }
391 
393  {
394  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup; }
395 
396  // anyway save for future reference
397  const CStatusMessage m = m_renderSettings.setAndSave(setup);
399 
400  // transfer to sim
401  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return; }
402  m_simulatorPlugin.second->setInterpolationSetupGlobal(setup);
403  }
404 
406  {
407  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
408  if (callsign.isEmpty()) { return {}; }
409  if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified()) { return {}; }
410  return m_simulatorPlugin.second->getInterpolationMessages(callsign);
411  }
412 
413  bool CContextSimulator::loadSimulatorPlugin(const CSimulatorPluginInfo &simulatorPluginInfo)
414  {
415  Q_ASSERT(this->getIContextApplication());
417  Q_ASSERT(!simulatorPluginInfo.isUnspecified());
418  Q_ASSERT(CThreadUtils::thisIsMainThread()); // only run in main thread
419 
420  // Is a plugin already loaded?
421  if (!m_simulatorPlugin.first.isUnspecified())
422  {
423  // This can happen, if a listener emitted simulatorStarted twice or two different simulators
424  // are running at the same time. In this case, we leave the loaded plugin and just return.
425  return false;
426  }
427 
428  if (!simulatorPluginInfo.isValid())
429  {
430  CLogMessage(this).error(u"Illegal plugin");
431  return false;
432  }
433 
434  ISimulatorFactory *factory = m_plugins->getFactory(simulatorPluginInfo.getIdentifier());
435  Q_ASSERT_X(factory, Q_FUNC_INFO, "no factory");
436 
437  // We assume we run in the same process as the own aircraft context
438  // Hence we pass in memory reference to own aircraft object
439  Q_ASSERT_X(this->getIContextOwnAircraft()->isUsingImplementingObject(), Q_FUNC_INFO,
440  "Need implementing object");
441  Q_ASSERT_X(this->getIContextNetwork()->isUsingImplementingObject(), Q_FUNC_INFO, "Need implementing object");
442  IOwnAircraftProvider *ownAircraftProvider = this->getRuntime()->getCContextOwnAircraft();
443  IRemoteAircraftProvider *renderedAircraftProvider = this->getRuntime()->getCContextNetwork();
444  IClientProvider *clientProvider = this->getRuntime()->getCContextNetwork();
445  ISimulator *simulator =
446  factory->create(simulatorPluginInfo, ownAircraftProvider, renderedAircraftProvider, clientProvider);
447  Q_ASSERT_X(simulator, Q_FUNC_INFO, "no simulator driver can be created");
448 
449  this->setRemoteAircraftProvider(renderedAircraftProvider);
450 
451  // use simulator info from ISimulator as it can access the emulated driver settings
452  CSimulatorInfo simInfo = simulator->getSimulatorInfo();
453  if (!simInfo.isSingleSimulator())
454  {
455  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Invalid simulator from plugin");
456  simInfo = CSimulatorInfo::p3d();
457  if (simulator->isEmulatedDriver())
458  {
459  CLogMessage(this).error(
460  u"Emulated driver does NOT provide valid simulator, using default '%1', plugin: '%2'")
461  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
462  }
463  else
464  {
465  CLogMessage(this).error(u"Invalid driver using default '%1', plugin: '%2'")
466  << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
467  }
468  }
469  Q_ASSERT_X(simInfo.isSingleSimulator(), Q_FUNC_INFO, "need single simulator");
470 
471  m_modelSetSimulator.set(simInfo);
472  const CAircraftModelList modelSetModels = this->getModelSet(); // synced
473  m_aircraftMatcher.setModelSet(modelSetModels, simInfo, true);
474  m_aircraftMatcher.setDefaultModel(simulator->getDefaultModel());
475 
476  bool c =
477  connect(simulator, &ISimulator::simulatorStatusChanged, this, &CContextSimulator::onSimulatorStatusChanged);
478  Q_ASSERT(c);
480  &CContextSimulator::onAddingRemoteAircraftFailed);
481  Q_ASSERT(c);
483  &CContextSimulator::onOwnSimulatorModelChanged);
484  Q_ASSERT(c);
487  Q_ASSERT(c);
490  Q_ASSERT(c);
493  Q_ASSERT(c);
495  Q_ASSERT(c);
497  Q_ASSERT(c);
499  Q_ASSERT(c);
500 
501  // disconnect for X-Plane FPS below 20
502  c = connect(simulator, &ISimulator::insufficientFrameRateDetected, this, [this](bool fatal) {
503  if (fatal) { emit this->vitalityLost(); }
504  });
505  Q_ASSERT(c);
508  Q_ASSERT(c);
509 
510  // log from context to simulator
512  &CContextSimulator::relayStatusMessageToSimulator);
513  Q_ASSERT(c);
515  &CContextSimulator::relayStatusMessageToSimulator);
516  Q_ASSERT(c);
517  Q_UNUSED(c)
518 
519  // Once the simulator signaled it is ready to simulate, add all known aircraft
520  m_initallyAddAircraft = true;
521  m_wasSimulating = false;
522  m_matchingMessages.clear();
523  m_failoverAddingCounts.clear();
524 
525  // try to connect to simulator
526  const bool connected = simulator->connectTo();
527  if (!connected)
528  {
529  CLogMessage(this).error(u"Simulator plugin connection to simulator '%1' failed")
530  << simulatorPluginInfo.toQString(true);
531  return false;
532  }
533 
534  // when everything is set up connected, update the current plugin info
535  m_simulatorPlugin.first = simulatorPluginInfo;
536  m_simulatorPlugin.second = simulator;
537  m_simulatorPlugin.second->setInterpolationSetupGlobal(m_renderSettings.get());
538 
539  // Emit signal after this function completes completely decoupled
540  QPointer<CContextSimulator> myself(this);
541  QTimer::singleShot(25, this, [=] {
542  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
543  if (m_simulatorPlugin.second)
544  {
545  CLogMessage(this).info(u"Simulator plugin loaded: '%1' connected: %2")
546  << simulatorPluginInfo.toQString(true) << boolToYesNo(connected);
547 
548  // use the real driver as this will also work eith emulated driver
549  emit this->simulatorPluginChanged(m_simulatorPlugin.second->getSimulatorPluginInfo());
550  emit this->simulatorChanged(m_simulatorPlugin.second->getSimulatorInfo());
551  }
552  });
553 
554  return true;
555  }
556 
557  bool CContextSimulator::listenForSimulator(const CSimulatorPluginInfo &simulatorInfo)
558  {
559  Q_ASSERT(this->getIContextApplication());
561  Q_ASSERT(!simulatorInfo.isUnspecified());
562  Q_ASSERT(m_simulatorPlugin.first.isUnspecified());
563 
564  if (!m_listenersThread.isRunning())
565  {
566  m_listenersThread.setObjectName("CContextSimulator: Thread for listener " + simulatorInfo.getIdentifier());
567  m_listenersThread.start(QThread::LowPriority);
568  }
569 
570  ISimulatorListener *listener = m_plugins->createListener(simulatorInfo.getIdentifier());
571  if (!listener) { return false; }
572 
573  if (listener->thread() != &m_listenersThread)
574  {
575  Q_ASSERT_X(!listener->parent(), Q_FUNC_INFO, "Objects with parent cannot be moved to thread");
576 
577  const bool c = connect(listener, &ISimulatorListener::simulatorStarted, this,
578  &CContextSimulator::onSimulatorStarted, Qt::QueuedConnection);
579  if (!c)
580  {
581  CLogMessage(this).error(u"Unable to use '%1'") << simulatorInfo.toQString();
582  return false;
583  }
584  listener->setProperty("isInitialized", true);
585  listener->moveToThread(&m_listenersThread);
586  }
587 
588  // start if not already running
589  if (!listener->isRunning())
590  {
592  Q_ASSERT_X(s, Q_FUNC_INFO, "cannot invoke method");
593  Q_UNUSED(s)
594  }
595  CLogMessage(this).info(u"Listening for simulator '%1'") << simulatorInfo.getIdentifier();
596  return true;
597  }
598 
599  void CContextSimulator::listenForAllSimulators()
600  {
601  const auto plugins = getAvailableSimulatorPlugins();
602  for (const CSimulatorPluginInfo &p : plugins)
603  {
604  Q_ASSERT(!p.isUnspecified());
605  if (p.isValid()) { this->listenForSimulator(p); }
606  }
607  }
608 
609  void CContextSimulator::unloadSimulatorPlugin()
610  {
611  if (!m_simulatorPlugin.first.isUnspecified())
612  {
613  ISimulator *simulator = m_simulatorPlugin.second;
614  if (simulator && simulator->isConnected())
615  {
616  // we are about to unload an connected simulator
617  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
619  }
620 
621  m_simulatorPlugin.second = nullptr;
622  m_simulatorPlugin.first = CSimulatorPluginInfo();
623 
624  Q_ASSERT(this->getIContextNetwork());
625  Q_ASSERT(this->getIContextNetwork()->isLocalObject());
626 
627  // unload and disconnect
628  if (simulator)
629  {
630  // disconnect signals and delete
631  simulator->disconnect(this);
632  simulator->unload();
633  simulator->deleteLater();
635  emit this->simulatorChanged(CSimulatorInfo());
636  }
637 
638  if (m_wasSimulating) { emit this->vitalityLost(); }
639  m_wasSimulating = false;
640  }
641  }
642 
643  void CContextSimulator::xCtxAddedRemoteAircraftReadyForModelMatching(const CSimulatedAircraft &remoteAircraft)
644  {
645  if (!this->isSimulatorPluginAvailable()) { return; }
646 
647  const CCallsign callsign = remoteAircraft.getCallsign();
648  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Remote aircraft with empty callsign");
649  if (callsign.isEmpty()) { return; }
650 
651  // here we find the best simulator model for a resolved model
652  // in the first step we already tried to find accurate ICAO codes etc.
653  // coming from CAirspaceMonitor::sendReadyForModelMatching
654  MatchingLog whatToLog = m_logMatchingMessages;
655  CStatusMessageList matchingMessages;
656  CStatusMessageList *pMatchingMessages = m_logMatchingMessages > 0 ? &matchingMessages : nullptr;
657  CAircraftModel aircraftModel =
658  m_aircraftMatcher.getClosestMatch(remoteAircraft, whatToLog, pMatchingMessages, true);
659  Q_ASSERT_X(remoteAircraft.getCallsign() == aircraftModel.getCallsign(), Q_FUNC_INFO, "Mismatching callsigns");
660 
661  // decide CG
662  const CLength cgModel = aircraftModel.getCG();
663  const CLength cgSim = m_simulatorPlugin.second->getSimulatorCGPerModelString(aircraftModel.getModelString());
664  const CSimulatorSettings simSettings = this->getSimulatorSettings();
665  switch (simSettings.getCGSource())
666  {
667  case CSimulatorSettings::CGFromSimulatorOnly: aircraftModel.setCG(cgSim); break;
668  case CSimulatorSettings::CGFromSimulatorFirst:
669  if (!cgSim.isNull()) { aircraftModel.setCG(cgSim); }
670  break;
671  case CSimulatorSettings::CGFromDBFirst:
672  if (cgModel.isNull()) { aircraftModel.setCG(cgSim); }
673  break;
674  // case CSimulatorSettings::CGFromDBOnly:
675  default: break; // leave CG from model alone
676  }
677 
678  const CLength overriddenCG =
679  m_simulatorPlugin.second->overriddenCGorDefault(CLength::null(), aircraftModel.getModelString());
680  if (!overriddenCG.isNull()) { aircraftModel.setCG(overriddenCG); }
681 
682  // model in provider
683  this->updateAircraftModel(callsign, aircraftModel, this->identifier());
684 
685  const CSimulatedAircraft aircraftAfterModelApplied =
686  this->getAircraftInRangeForCallsign(remoteAircraft.getCallsign());
687  if (!aircraftAfterModelApplied.hasModelString())
688  {
689  if (!aircraftAfterModelApplied.hasCallsign()) { return; } // removed
690  if (this->isAircraftInRange(aircraftAfterModelApplied.getCallsign()))
691  {
692  return;
693  } // removed, but callsig, we did crosscheck
694 
695  // callsign, but no model string
696  CLogMessage(this).error(u"Matching error for '%1', no model string")
697  << aircraftAfterModelApplied.getCallsign().asString();
698 
699  CSimulatedAircraft brokenAircraft(aircraftAfterModelApplied);
700  brokenAircraft.setEnabled(false);
701  brokenAircraft.setRendered(false);
702  CCallsign::addLogDetailsToList(
703  pMatchingMessages, callsign,
704  QStringLiteral("Cannot add remote aircraft, no model string: '%1'").arg(brokenAircraft.toQString()));
705  emit this->aircraftRenderingChanged(brokenAircraft);
706  return;
707  }
708 
709  // here the model is added to the simulator
710  m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraftAfterModelApplied);
711  CCallsign::addLogDetailsToList(
712  pMatchingMessages, callsign,
713  QStringLiteral("Logically added remote aircraft: %1").arg(aircraftAfterModelApplied.toQString()));
714 
715  this->clearMatchingMessages(callsign);
716  this->addMatchingMessages(callsign, matchingMessages);
717 
718  // done
719  emit this->modelMatchingCompleted(aircraftAfterModelApplied);
720  }
721 
722  void CContextSimulator::xCtxRemovedRemoteAircraft(const CCallsign &callsign)
723  {
724  if (!this->isSimulatorAvailable()) { return; }
725  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
726  m_failoverAddingCounts.remove(callsign);
727  }
728 
729  void CContextSimulator::onSimulatorStatusChanged(ISimulator::SimulatorStatus status)
730  {
731  m_wasSimulating = m_wasSimulating || status.testFlag(ISimulator::Simulating);
732  if (m_initallyAddAircraft && status.testFlag(ISimulator::Simulating))
733  {
734  // use network to initally add aircraft
735  IContextNetwork *networkContext = this->getIContextNetwork();
736  Q_ASSERT_X(networkContext, Q_FUNC_INFO, "Need context");
737  Q_ASSERT_X(networkContext->isLocalObject(), Q_FUNC_INFO, "Need local object");
738 
739  // initially add aircraft
740  const CSimulatedAircraftList aircraft = networkContext->getAircraftInRange();
741  for (const CSimulatedAircraft &simulatedAircraft : aircraft)
742  {
743  SWIFT_VERIFY_X(!simulatedAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "Need callsign");
744  this->xCtxAddedRemoteAircraftReadyForModelMatching(simulatedAircraft);
745  }
746  m_initallyAddAircraft = false;
747  }
748 
749  if (!status.testFlag(ISimulator::Connected))
750  {
751  // we got disconnected, plugin no longer needed
752  this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
753  this->unloadSimulatorPlugin();
754  this->restoreSimulatorPlugins();
755 
756  if (m_wasSimulating) { emit this->vitalityLost(); }
757  m_wasSimulating = false;
758  }
759 
760  emit this->simulatorStatusChanged(status);
761  }
762 
763  void CContextSimulator::xCtxTextMessagesReceived(const network::CTextMessageList &textMessages)
764  {
765  if (!this->isSimulatorAvailable()) { return; }
766  if (!this->getIContextOwnAircraft()) { return; }
767 
768  const CSimulatorMessagesSettings settings = m_messageSettings.getThreadLocal();
769  const CSimulatedAircraft ownAircraft = this->getIContextOwnAircraft()->getOwnAircraft();
770  for (const auto &tm : textMessages)
771  {
772  if (!settings.relayThisTextMessage(tm, ownAircraft)) { continue; }
773  m_simulatorPlugin.second->displayTextMessage(tm);
774  }
775  }
776 
777  void CContextSimulator::xCtxChangedRemoteAircraftModel(const CSimulatedAircraft &aircraft,
778  const swift::misc::CIdentifier &originator)
779  {
780  if (CIdentifiable::isMyIdentifier(originator)) { return; }
781  if (!this->isSimulatorAvailable()) { return; }
782  m_simulatorPlugin.second->changeRemoteAircraftModel(aircraft);
783  }
784 
785  void CContextSimulator::xCtxChangedOwnAircraftModel(const CAircraftModel &aircraftModel,
786  const CIdentifier &originator)
787  {
788  if (CIdentifiable::isMyIdentifier(originator)) { return; }
789  if (!this->isSimulatorAvailable()) { return; }
790 
791  emit this->ownAircraftModelChanged(aircraftModel);
792  }
793 
794  void CContextSimulator::xCtxChangedRemoteAircraftEnabled(const CSimulatedAircraft &aircraft)
795  {
796  if (!this->isSimulatorAvailable()) { return; }
797  m_simulatorPlugin.second->changeRemoteAircraftEnabled(aircraft);
798  }
799 
800  void CContextSimulator::xCtxNetworkConnectionStatusChanged(const CConnectionStatus &from,
801  const CConnectionStatus &to)
802  {
803  Q_UNUSED(from)
804  SWIFT_VERIFY_X(this->getIContextNetwork(), Q_FUNC_INFO, "Missing network context");
805  SWIFT_VERIFY_X(this->getIContextOwnAircraft(), Q_FUNC_INFO, "Missing own aircraft context");
806  if (to.isConnected() && this->getIContextNetwork())
807  {
808  m_networkSessionId = this->getIContextNetwork()->getConnectedServer().getServerSessionId(false);
809  if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
810  {
811  m_simulatorPlugin.second->setFlightNetworkConnected(true);
812  m_simulatorPlugin.second->setOwnCallsign(
813  this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
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  m_simulatorPlugin.second->setOwnCallsign({});
828  }
829  }
830  }
831 
832  void CContextSimulator::onAddingRemoteAircraftFailed(const CSimulatedAircraft &remoteAircraft, bool disabled,
833  bool requestFailover, const CStatusMessage &message)
834  {
835  if (!this->isSimulatorAvailable()) { return; }
836  if (disabled) { m_aircraftMatcher.addingRemoteModelFailed(remoteAircraft); }
837 
838  const CCallsign cs = remoteAircraft.getCallsign();
839  if (cs.isEmpty()) { return; }
840 
841  bool failover = requestFailover;
842  if (requestFailover)
843  {
844  const CAircraftMatcherSetup setup = m_aircraftMatcher.getSetup();
845  if (setup.doModelAddFailover())
846  {
847  const int trial = m_failoverAddingCounts.value(cs, 0);
848  if (trial < MaxModelAddedFailoverTrials)
849  {
850  m_failoverAddingCounts[cs] = trial + 1;
851  this->doMatchingAgain(cs); // has its own single shot logic
852  }
853  else
854  {
855  failover = false;
856  CLogMessage(this).warning(u"Model for '%1' failed adding, tried %2 time(s) to resolve, giving up")
857  << cs.toQString(true) << (trial + 1);
858  }
859  }
860  }
861  emit this->addingRemoteModelFailed(remoteAircraft, disabled, failover, message);
862  }
863 
864  void CContextSimulator::xCtxUpdateSimulatorCockpitFromContext(const CSimulatedAircraft &ownAircraft,
865  const CIdentifier &originator)
866  {
867  if (!this->isSimulatorAvailable()) { return; }
868  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
869 
870  // update
871  m_simulatorPlugin.second->updateOwnSimulatorCockpit(ownAircraft, originator);
872  }
873 
874  void CContextSimulator::xCtxUpdateSimulatorSelcalFromContext(const CSelcal &selcal, const CIdentifier &originator)
875  {
876  if (!this->isSimulatorAvailable()) { return; }
877  if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName()) { return; }
878 
879  // update
880  m_simulatorPlugin.second->updateOwnSimulatorSelcal(selcal, originator);
881  }
882 
883  void CContextSimulator::xCtxNetworkRequestedNewAircraft(const CCallsign &callsign, const QString &aircraftIcao,
884  const QString &airlineIcao, const QString &livery)
885  {
886  if (m_networkSessionId.isEmpty()) { return; }
887  m_aircraftMatcher.evaluateStatisticsEntry(m_networkSessionId, callsign, aircraftIcao, airlineIcao, livery);
888  }
889 
890  void CContextSimulator::relayStatusMessageToSimulator(const swift::misc::CStatusMessage &message)
891  {
892  if (!this->isSimulatorAvailable()) { return; }
893  if (!sApp || sApp->isShuttingDown()) { return; }
894  const CSimulatorMessagesSettings simMsg = m_messageSettings.getThreadLocal();
895  if (simMsg.relayThisStatusMessage(message) && m_simulatorPlugin.second)
896  {
897  m_simulatorPlugin.second->displayStatusMessage(message);
898  }
899  }
900 
901  void CContextSimulator::changeEnabledSimulators()
902  {
903  CSimulatorPluginInfo currentPluginInfo = m_simulatorPlugin.first;
904  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
905 
906  // Unload the current plugin, if it is no longer enabled
907  if (!currentPluginInfo.isUnspecified() && !enabledSimulators.contains(currentPluginInfo.getIdentifier()))
908  {
909  unloadSimulatorPlugin();
911  }
912  restoreSimulatorPlugins();
913  }
914 
915  void CContextSimulator::restoreSimulatorPlugins()
916  {
917  if (!m_simulatorPlugin.first.isUnspecified()) { return; }
918 
919  this->stopSimulatorListeners();
920  const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
921  const CSimulatorPluginInfoList allSimulators = m_plugins->getAvailableSimulatorPlugins();
922  for (const CSimulatorPluginInfo &s : allSimulators)
923  {
924  if (enabledSimulators.contains(s.getIdentifier())) { startSimulatorPlugin(s); }
925  }
926  }
927 
929  {
930  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
931  return m_matchingMessages[callsign];
932  }
933 
935  {
936  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
937  return m_logMatchingMessages;
938  }
939 
941  {
942  if (isDebugEnabled())
943  {
944  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << matchingLogToString(enabled);
945  }
946  if (m_logMatchingMessages == enabled) { return; }
947  m_logMatchingMessages = enabled;
949  }
950 
952  {
953  if (isDebugEnabled())
954  {
955  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << missingOnly;
956  }
957  const CMatchingStatistics statistics = m_aircraftMatcher.getCurrentStatistics();
958  return missingOnly ? statistics.findMissingOnly() : statistics;
959  }
960 
962  {
963  if (isDebugEnabled())
964  {
965  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup.toQString();
966  }
967  m_aircraftMatcher.setSetup(setup);
968  const CStatusMessage msg = m_matchingSettings.setAndSave(setup);
970  }
971 
973  {
974  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
975  return m_aircraftMatcher.getSetup();
976  }
977 
979  {
980  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
981  bool added = add;
982  if (add)
983  {
984  m_simulatorPlugin.second->setTestMode(true);
985  added = m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraft);
986  }
987  else
988  {
989  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(aircraft.getCallsign());
990  m_simulatorPlugin.second->setTestMode(false); // AFTER we have removed it
991  }
992  return added;
993  }
994 
996  const CAircraftParts &parts)
997  {
998  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
999  CAircraftSituation s = situation; // make sure to have correct callsign
1000  s.setCallsign(cs);
1001  return m_simulatorPlugin.second->testSendSituationAndParts(cs, s, parts);
1002  }
1003 
1004  bool CContextSimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
1005  {
1006  Q_UNUSED(originator)
1007  if (commandLine.isEmpty()) { return false; }
1008  CSimpleCommandParser parser({
1009  ".plugin", ".drv", ".driver", // forwarded to driver
1010  ".ris" // rendering interpolator setup
1011  });
1012  parser.parse(commandLine);
1013  if (!parser.isKnownCommand()) { return false; }
1014  if (parser.matchesCommand("ris"))
1015  {
1017  const QString p1 = parser.part(1);
1018  if (!parser.hasPart(2)) { return false; }
1019  const bool on = stringToBool(parser.part(2));
1020  if (p1 == "debug") { rs.setSimulatorDebuggingMessages(on); }
1021  else if (p1 == "parts") { rs.setEnabledAircraftParts(on); }
1022  else { return false; }
1024  CLogMessage(this, CLogCategories::cmdLine()).info(u"Setup is: '%1'") << rs.toQString(true);
1025  return true;
1026  }
1027  if (parser.matchesCommand("plugin") || parser.matchesCommand("drv") || parser.matchesCommand("driver"))
1028  {
1029  if (!m_simulatorPlugin.second) { return false; }
1030  return m_simulatorPlugin.second->parseCommandLine(commandLine, originator);
1031  }
1032  return false;
1033  }
1034 
1036  {
1037  if (!this->isSimulatorAvailable() || !m_simulatorPlugin.second) { return nullptr; }
1038  return m_simulatorPlugin.second;
1039  }
1040 
1042  {
1043  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
1044  if (!m_simulatorPlugin.second) { return false; }
1045  return m_simulatorPlugin.second->followAircraft(callsign);
1046  }
1047 
1049  {
1050  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1051  if (!m_simulatorPlugin.second) { return; }
1052  m_simulatorPlugin.second->recalculateAllAircraft();
1053  }
1054 
1056  {
1057  CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1058  if (aircraft.getCallsign() != callsign) { return false; } // not found
1059  if (!this->isSimulatorAvailable()) { return false; }
1060 
1061  m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
1062  aircraft.setModel(aircraft.getNetworkModel()); // like originally from network
1063  QPointer<CContextSimulator> myself(this);
1064  QTimer::singleShot(1000, this, [=] {
1065  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1066  this->xCtxAddedRemoteAircraftReadyForModelMatching(aircraft);
1067  });
1068  return true;
1069  }
1070 
1072  {
1073  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << situation; }
1074  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return false; }
1075  return m_simulatorPlugin.second->requestElevationBySituation(situation);
1076  }
1077 
1079  const CLength &range) const
1080  {
1081  if (isDebugEnabled())
1082  {
1084  << Q_FUNC_INFO << reference.convertToQString(true) << range;
1085  }
1086  if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected()) { return CElevationPlane::null(); }
1087  return m_simulatorPlugin.second->findClosestElevationWithinRange(reference, range);
1088  }
1089 
1091  {
1092  if (isDebugEnabled()) { CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO; }
1093  const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
1094  if (callsigns.isEmpty()) { return 0; }
1095  int delayMs = 25;
1096  QPointer<CContextSimulator> myself(this);
1097  for (const CCallsign &cs : callsigns)
1098  {
1099  QTimer::singleShot(delayMs, this, [=] {
1100  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1101  this->doMatchingAgain(cs);
1102  });
1103  delayMs += 25;
1104  }
1105  return callsigns.size();
1106  }
1107 
1109  {
1110  if (isDebugEnabled())
1111  {
1112  CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign.asString();
1113  }
1114  if (!this->isAircraftInRange(callsign)) { return false; }
1115  if (!this->isSimulatorAvailable()) { return false; }
1116 
1117  QPointer<CContextSimulator> myself(this);
1118  QTimer::singleShot(2500, this, [=] {
1119  if (!sApp || sApp->isShuttingDown() || !myself) { return; }
1120  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1121  if (!aircraft.hasCallsign()) { return; } // no longer valid
1122  CSimulatedAircraft resetAircraft(aircraft);
1123  resetAircraft.resetToNetworkModel();
1124  resetAircraft.setEnabled(true);
1125  this->xCtxAddedRemoteAircraftReadyForModelMatching(resetAircraft);
1126  });
1127  return true;
1128  }
1129 
1130  void CContextSimulator::onSimulatorStarted(const CSimulatorPluginInfo &info)
1131  {
1132  this->stopSimulatorListeners();
1133  this->loadSimulatorPlugin(info);
1134 
1135  // if we have enabled messages, we will disable if size getting too high
1136  if (m_logMatchingMessages)
1137  {
1138  const QPointer<CContextSimulator> myself(this);
1139  QTimer::singleShot(5000, this, [=] {
1140  if (!myself) { return; }
1141  if (m_aircraftMatcher.getModelSetCount() > MatchingLogMaxModelSetSize)
1142  {
1143  const MatchingLog log = CBuildConfig::isDebugBuild() ? MatchingLogAll : MatchingLogSimplified;
1144  this->enableMatchingMessages(log);
1145  }
1146  });
1147  }
1148  }
1149 
1150  void CContextSimulator::onOwnSimulatorModelChanged(const CAircraftModel &model)
1151  {
1152  CAircraftModel lookupModel(model);
1153  if (!lookupModel.isLoadedFromDb()) { lookupModel = this->reverseLookupModel(model); }
1154  emit this->ownAircraftModelChanged(lookupModel);
1155  }
1156 
1157  void CContextSimulator::stopSimulatorListeners()
1158  {
1160  {
1161  ISimulatorListener *listener = m_plugins->getListener(info.getIdentifier());
1162  if (listener)
1163  {
1164  const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::stop);
1165  Q_ASSERT_X(s, Q_FUNC_INFO, "Cannot invoke stop");
1166  Q_UNUSED(s)
1167  }
1168  }
1169  }
1170 
1171  void CContextSimulator::addMatchingMessages(const CCallsign &callsign, const CStatusMessageList &messages)
1172  {
1173  if (callsign.isEmpty()) { return; }
1174  if (messages.isEmpty()) { return; }
1175  if (!m_logMatchingMessages) { return; }
1176  if (m_matchingMessages.contains(callsign))
1177  {
1178  CStatusMessageList &msgs = m_matchingMessages[callsign];
1179  msgs.push_back(messages);
1180  }
1181  else { m_matchingMessages.insert(callsign, messages); }
1182  }
1183 
1184  void CContextSimulator::clearMatchingMessages(const CCallsign &callsign)
1185  {
1186  if (callsign.isEmpty()) { return; }
1187  m_matchingMessages.remove(callsign);
1188  }
1189 
1190  CAircraftModel CContextSimulator::reverseLookupModel(const CAircraftModel &model)
1191  {
1192  bool modified = false;
1193  const CAircraftModel reverseModel =
1194  CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified);
1195  return reverseModel;
1196  }
1197 
1198  void CContextSimulator::initByLastUsedModelSet()
1199  {
1200  // no models in matcher, but in cache, we can set them as default
1201  const CSimulatorInfo simulator(m_modelSetSimulator.get());
1202  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
1203  const CAircraftModelList models(this->getModelSet()); // synced
1204  CLogMessage(this).info(u"Init aircraft matcher with %1 models from set for '%2'")
1205  << models.size() << simulator.toQString();
1206  m_aircraftMatcher.setModelSet(models, simulator, false);
1207  }
1208 } // 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.
int getModelSetCount() const
Model set count.
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.
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:481
context::CContextOwnAircraft * getCContextOwnAircraft()
Context for own aircraft.
Definition: corefacade.cpp:495
Manages plugins for the simulator context.
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:633
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:1271
void stop()
Stops listening.
Definition: simulator.cpp:1288
bool followAircraft(const swift::misc::aviation::CCallsign &callsign)
Follow aircraft im simulator view.
bool setSimulatorSettings(const swift::misc::simulation::settings::CSimulatorSettings &settings, const swift::misc::simulation::CSimulatorInfo &simulator)
Set settings for give simulator.
swift::misc::CStatusMessageList verifyPrerequisites() const
Verify prerequisites for simulation like an existing model set.
bool resetToModelMatchingAircraft(const swift::misc::aviation::CCallsign &callsign)
Reset model by matching it again.
ISimulator::SimulatorStatus getSimulatorStatus() const
Simulator combined status.
void setInterpolationAndRenderingSetupGlobal(const swift::misc::simulation::CInterpolationAndRenderingSetupGlobal &setup)
Set the global setup.
swift::misc::simulation::CAircraftMatcherSetup getMatchingSetup() const
Get matching setup.
swift::misc::simulation::CAircraftModelList getModelSet() const
Installed models in simulator eco system.
bool isKnownModelInSet(const QString &modelString) const
Known model?
swift::misc::simulation::CInterpolationSetupList getInterpolationAndRenderingSetupsPerCallsign() const
Get all setups per callsign.
swift::misc::simulation::CAircraftModelList getModelSetModelsStartingWith(const QString &modelString) const
Models for model string.
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)
bool startSimulatorPlugin(const swift::misc::simulation::CSimulatorPluginInfo &simulatorInfo)
Load and start specific simulator plugin.
void disableModelsForMatching(const swift::misc::simulation::CAircraftModelList &removedModels, bool incremental)
Remove a model for matching.
swift::misc::simulation::CSimulatorInternals getSimulatorInternals() const
Simulator setup.
swift::misc::CStatusMessageList getMatchingMessages(const swift::misc::aviation::CCallsign &callsign) const
Get mapping messages.
QStringList getModelSetCompleterStrings(bool sorted) const
Model set completer string.
bool triggerModelSetValidation(const swift::misc::simulation::CSimulatorInfo &simulator)
Trigger model set validation.
int removeModelsFromSet(const swift::misc::simulation::CAircraftModelList &removeModels)
Remove models from set.
static void registerHelp()
Register dot commands.
bool testUpdateRemoteAircraft(const swift::misc::aviation::CCallsign &cs, const swift::misc::aviation::CAircraftSituation &situation, const swift::misc::aviation::CAircraftParts &parts)
Test update remote aircraft.
void setModelSetLoaderSimulator(const swift::misc::simulation::CSimulatorInfo &simulator)
Set the model set loader simulator directly.
void recalculateAllAircraft()
Recalculate all aircraft.
swift::misc::simulation::CInterpolationAndRenderingSetupGlobal getInterpolationAndRenderingSetupGlobal() const
The global setup.
bool testRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft, bool add)
Test a remote aircraft.
bool doMatchingAgain(const swift::misc::aviation::CCallsign &callsign)
Repeat the matching callsign.
bool requestElevationBySituation(const swift::misc::aviation::CAircraftSituation &situation)
Request elevation, there is no guarantee the requested elevation will be available in the provider.
void restoreDisabledModels()
Restore the models removed with CAircraftMatcher::disableModelForMatching.
swift::misc::simulation::CSimulatorInfo getModelSetLoaderSimulator() const
Get the model set loader simulator directly.
swift::misc::simulation::CSimulatorPluginInfoList getAvailableSimulatorPlugins() const
Return list of available simulator plugins.
void gracefulShutdown()
Gracefully shut down, e.g. for plugin unloading.
swift::misc::simulation::CSimulatorPluginInfo getSimulatorPluginInfo() const
Simulator info, currently loaded plugin.
swift::misc::simulation::CSimulatorInfo simulatorsWithInitializedModelSet() const
Simulators which have an initialized model set.
swift::misc::CStatusMessageList getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const
Interpolation messages.
swift::misc::simulation::settings::CSimulatorSettings getSimulatorSettings() const
Get the current simulator settings.
void setMatchingSetup(const swift::misc::simulation::CAircraftMatcherSetup &setup)
Set matching setup.
int checkListeners()
Check all listeners enabled if simulator is connected.
swift::misc::simulation::CMatchingStatistics getCurrentMatchingStatistics(bool missingOnly) const
Current matching statistics.
bool isValidationInProgress() const
Validation in progress.
QPointer< ISimulator > simulator() const
Access to simulator (i.e. the plugin)
swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign getInterpolationAndRenderingSetupPerCallsignOrDefault(const swift::misc::aviation::CCallsign &callsign) const
Get the setup for callsign, if not existing the global setup.
int getModelSetCount() const
Number of installed models in simulator eco system.
swift::misc::simulation::MatchingLog isMatchingMessagesEnabled() const
Enabled mapping logging?
void stopSimulatorPlugin(const swift::misc::simulation::CSimulatorPluginInfo &simulatorInfo)
Stop listener or unload the given plugin (if currently loaded)
bool setInterpolationAndRenderingSetupsPerCallsign(const swift::misc::simulation::CInterpolationSetupList &setups, bool ignoreSameAsGlobal)
Set all setups per callsign.
QStringList getModelSetStrings() const
Model strings.
void enableMatchingMessages(swift::misc::simulation::MatchingLog enabled)
Enable mapping logging.
bool isSimulatorPluginAvailable() const
Simulator plugin available?
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.
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.
void clear()
bool contains(const Key &key) const const
QMap< Key, T >::iterator insert(QMap< Key, T >::const_iterator pos, const Key &key, const T &value)
QMap< Key, T >::size_type remove(const Key &key)
T value(const Key &key, const T &defaultValue) const const
bool invokeMethod(QObject *context, Functor &&function, Args &&... arguments)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
void setObjectName(QAnyStringView name)
void clear()
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
CaseInsensitive
QueuedConnection
bool isRunning() const const
void quit()
void start(QThread::Priority priority)
bool wait(QDeadlineTimer deadline)
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26