swift
Loading...
Searching...
No Matches
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"
23#include "core/simulator.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
39using namespace swift::config;
40using namespace swift::core::db;
41using namespace swift::misc;
42using namespace swift::misc::physical_quantities;
43using namespace swift::misc::aviation;
44using namespace swift::misc::network;
45using namespace swift::misc::simulation;
46using namespace swift::misc::simulation::xplane;
47using namespace swift::misc::geo;
48using namespace swift::misc::weather;
49using namespace swift::misc::simulation;
50using namespace swift::misc::simulation::settings;
51using namespace swift::misc::simulation::data;
52using namespace std::chrono_literals;
53
54namespace swift::core::context
55{
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
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, [=, this] {
76 if (!myself)
77 {
78 return;
79 }
80 this->initByLastUsedModelSet();
81 m_aircraftMatcher.setSetup(m_matchingSettings.get());
82 if (m_aircraftMatcher.getModelSetCount() <= MatchingLogMaxModelSetSize)
83 {
84 this->enableMatchingMessages(logMatchingMessages);
85 }
86 });
87
88 // Validation
89 m_validator = new CBackgroundValidation(this);
90 this->setValidator(this->getSimulatorPluginInfo().getSimulator());
91 connect(this, &CContextSimulator::simulatorChanged, this, &CContextSimulator::setValidator);
94
95 m_validator->start(QThread::LowestPriority);
96 using namespace std::chrono_literals;
97 m_validator->startUpdating(60s);
98 }
99
100 // For validation we need simulator directory and model directory
101 // this function is called at start (simulator=0) and when there is an active connection to a simulator
102 void CContextSimulator::setValidator(const CSimulatorInfo &simulator)
103 {
104 if (simulator.isSingleSimulator())
105 {
106 const QString simDir = m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator);
107 const QStringList modelDirList = m_multiSimulatorSettings.getModelDirectoriesOrDefault(simulator);
108 m_validator->setCurrentSimulator(simulator, simDir, modelDirList);
109 }
110 else
111 {
112 m_validator->setCurrentSimulator(CSimulatorInfo::None, {}, {});
113 }
114 }
115
117 {
118 return m_simulatorPlugin.second && IContextSimulator::isSimulatorAvailable();
119 }
120
122
124 {
125 if (m_validator)
126 {
127 disconnect(m_validator);
128 m_validator->quitAndWait();
129 m_validator->deleteLater();
130 m_validator = nullptr;
131 }
132 this->stopSimulatorListeners();
133 this->disconnect();
134 this->unloadSimulatorPlugin();
135
136 // give listeners a head start
137 // if simconnect is running remotely it can take a while until it shutdowns
138 m_listenersThread.quit();
139 m_listenersThread.wait(10 * 1000);
140 }
141
143 {
144 if (!m_plugins)
145 {
146 return {};
147 }
148 return m_plugins->getAvailableSimulatorPlugins();
149 }
150
152 {
154 if (!p.isValid())
155 {
156 return {};
157 }
158 const CSimulatorInfo sim = p.getSimulatorInfo();
159 if (!sim.isSingleSimulator())
160 {
161 return {};
162 }
163 return m_multiSimulatorSettings.getSettings(sim);
164 }
165
167 {
168 if (!simulator.isSingleSimulator())
169 {
170 return false;
171 }
172 const CSimulatorSettings simSettings = m_multiSimulatorSettings.getSettings(simulator);
173 if (simSettings == settings)
174 {
175 return false;
176 }
177 const CStatusMessage msg = m_multiSimulatorSettings.setSettings(settings, simulator);
179 emit this->simulatorSettingsChanged();
180 return true;
181 }
182
184 {
185 return this->listenForSimulator(simulatorInfo);
186 }
187
189 {
190 if (!m_simulatorPlugin.first.isUnspecified() && m_simulatorPlugin.first == simulatorInfo)
191 {
192 this->unloadSimulatorPlugin();
193 }
194
195 ISimulatorListener *listener = m_plugins->getListener(simulatorInfo.getIdentifier());
196 Q_ASSERT(listener);
198 }
199
201 {
202 if (isDebugEnabled())
203 {
204 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
205 }
206 if (!m_plugins)
207 {
208 return 0;
209 }
210 return m_plugins->checkAvailableListeners();
211 }
212
213 ISimulator::SimulatorStatus CContextSimulator::getSimulatorStatus() const
214 {
215 if (isDebugEnabled())
216 {
217 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
218 }
219 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
220 {
222 }
223 return m_simulatorPlugin.second->getSimulatorStatus();
224 }
225
227 {
228 static const CSimulatorPluginInfo unspecified;
229 if (isDebugEnabled())
230 {
231 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
232 }
233 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
234 {
235 return unspecified;
236 }
237 if (m_simulatorPlugin.first.getSimulator().contains("emulated", Qt::CaseInsensitive))
238 {
239 return m_simulatorPlugin.second->getSimulatorPluginInfo();
240 }
241 return m_simulatorPlugin.first;
242 }
243
245 {
246 if (isDebugEnabled())
247 {
248 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
249 }
250 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
251 {
252 return {};
253 }
254 return m_simulatorPlugin.second->getSimulatorInternals();
255 }
256
272
274 {
275 if (isDebugEnabled())
276 {
277 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
278 }
279 if (m_simulatorPlugin.second)
280 {
281 if (m_simulatorPlugin.second->isConnected())
282 {
283 if (m_simulatorPlugin.second->isEmulatedDriver())
284 {
285 // specialized version returning the "emulated info"
287 }
288 return m_simulatorPlugin.first.getSimulatorInfo();
289 }
290 }
291 return m_modelSetSimulator.get();
292 }
293
295 {
296 Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
297 if (isDebugEnabled())
298 {
299 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
300 }
301 if (this->isSimulatorAvailable())
302 {
303 return;
304 } // if a plugin is loaded, do ignore this
305 m_modelSetSimulator.set(simulator);
306 const CAircraftModelList models = this->getModelSet(); // cache synced
307 m_aircraftMatcher.setModelSet(models, simulator, false);
308 }
309
318
320 {
321 if (isDebugEnabled())
322 {
323 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
324 }
326 const CSimulatorInfo simulators = this->simulatorsWithInitializedModelSet();
327 if (simulators.isNoSimulator())
328 {
329 msgs.push_back(CStatusMessage(this).validationError(
330 u"No model set so far, you need at least one model set. Hint: You can create a model set in the "
331 u"mapping tool, or copy an existing set in the launcher."));
332 }
333 return msgs;
334 }
335
337 {
338 if (isDebugEnabled())
339 {
340 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
341 }
342 return this->getModelSet().getModelStringList(false);
343 }
344
345 bool CContextSimulator::isKnownModelInSet(const QString &modelString) const
346 {
347 if (isDebugEnabled())
348 {
349 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
350 }
351 const bool known = this->getModelSet().containsModelString(modelString);
352 return known;
353 }
354
356 {
357 if (isDebugEnabled())
358 {
359 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
360 }
361 if (removeModels.isEmpty())
362 {
363 return 0;
364 }
365 const CSimulatorInfo simulator = m_modelSetSimulator.get();
366 if (!simulator.isSingleSimulator())
367 {
368 return 0;
369 }
370
372 CAircraftModelList models =
374 const int removed = models.removeModelsWithString(removeModels, Qt::CaseInsensitive);
375 if (removed > 0)
376 {
378 }
379 return removed;
380 }
381
383 {
384 if (isDebugEnabled())
385 {
386 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << sorted;
387 }
388 return this->getModelSet().toCompleterStrings(sorted);
389 }
390
392 {
393 if (isDebugEnabled())
394 {
395 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
396 }
397 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
398 {
399 return 0;
400 }
401 return this->getModelSet().size();
402 }
403
404 void CContextSimulator::disableModelsForMatching(const CAircraftModelList &removedModels, bool incremental)
405 {
406 if (isDebugEnabled())
407 {
408 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
409 }
410 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
411 {
412 return;
413 }
414 m_aircraftMatcher.disableModelsForMatching(removedModels, incremental);
415 }
416
418 {
419 if (isDebugEnabled())
420 {
421 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
422 }
423 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
424 {
425 return {};
426 }
427 return m_aircraftMatcher.getDisabledModelsForMatching();
428 }
429
431 {
432 if (isDebugEnabled())
433 {
434 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
435 }
436 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
437 {
438 return;
439 }
440 m_aircraftMatcher.restoreDisabledModels();
441 }
442
444 {
445 if (isDebugEnabled())
446 {
447 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
448 }
449 if (!m_validator)
450 {
451 return false;
452 }
453 return m_validator->isValidating();
454 }
455
457 {
458 if (isDebugEnabled())
459 {
460 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
461 }
462 if (!m_validator)
463 {
464 return false;
465 }
466 const QString simDir =
467 simulator.isSingleSimulator() ? m_multiSimulatorSettings.getSimulatorDirectoryOrDefault(simulator) : "";
468 return m_validator->triggerValidation(simulator, simDir);
469 }
470
472 {
473 if (isDebugEnabled())
474 {
475 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << modelString;
476 }
477 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
478 {
479 return {};
480 }
481
482 return this->getModelSet().findModelsStartingWith(modelString);
483 }
484
486 {
487 if (isDebugEnabled())
488 {
489 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
490 }
491 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
492 {
493 return m_renderSettings.get();
494 }
495 return m_simulatorPlugin.second->getInterpolationSetupGlobal();
496 }
497
499 {
500 if (isDebugEnabled())
501 {
502 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
503 }
504 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
505 {
506 return {};
507 }
508 return m_simulatorPlugin.second->getInterpolationSetupsPerCallsign();
509 }
510
513 {
514 if (isDebugEnabled())
515 {
516 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
517 }
518 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
519 {
520 return {};
521 }
522 return m_simulatorPlugin.second->getInterpolationSetupPerCallsignOrDefault(callsign);
523 }
524
526 bool ignoreSameAsGlobal)
527 {
528 if (isDebugEnabled())
529 {
530 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
531 }
532 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
533 {
534 return false;
535 }
536 return m_simulatorPlugin.second->setInterpolationSetupsPerCallsign(setups, ignoreSameAsGlobal);
537 }
538
540 {
541 if (isDebugEnabled())
542 {
543 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup;
544 }
545
546 // anyway save for future reference
547 const CStatusMessage m = m_renderSettings.setAndSave(setup);
549
550 // transfer to sim
551 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
552 {
553 return;
554 }
555 m_simulatorPlugin.second->setInterpolationSetupGlobal(setup);
556 }
557
559 {
560 if (isDebugEnabled())
561 {
562 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
563 }
564 if (callsign.isEmpty())
565 {
566 return {};
567 }
568 if (!m_simulatorPlugin.second || m_simulatorPlugin.first.isUnspecified())
569 {
570 return {};
571 }
572 return m_simulatorPlugin.second->getInterpolationMessages(callsign);
573 }
574
575 bool CContextSimulator::loadSimulatorPlugin(const CSimulatorPluginInfo &simulatorPluginInfo)
576 {
577 Q_ASSERT(this->getIContextApplication());
579 Q_ASSERT(!simulatorPluginInfo.isUnspecified());
580 Q_ASSERT(CThreadUtils::thisIsMainThread()); // only run in main thread
581
582 // Is a plugin already loaded?
583 if (!m_simulatorPlugin.first.isUnspecified())
584 {
585 // This can happen, if a listener emitted simulatorStarted twice or two different simulators
586 // are running at the same time. In this case, we leave the loaded plugin and just return.
587 return false;
588 }
589
590 if (!simulatorPluginInfo.isValid())
591 {
592 CLogMessage(this).error(u"Illegal plugin");
593 return false;
594 }
595
596 ISimulatorFactory *factory = m_plugins->getFactory(simulatorPluginInfo.getIdentifier());
597 Q_ASSERT_X(factory, Q_FUNC_INFO, "no factory");
598
599 // We assume we run in the same process as the own aircraft context
600 // Hence we pass in memory reference to own aircraft object
601 Q_ASSERT_X(this->getIContextOwnAircraft()->isUsingImplementingObject(), Q_FUNC_INFO,
602 "Need implementing object");
603 Q_ASSERT_X(this->getIContextNetwork()->isUsingImplementingObject(), Q_FUNC_INFO, "Need implementing object");
604 IOwnAircraftProvider *ownAircraftProvider = this->getRuntime()->getCContextOwnAircraft();
605 IRemoteAircraftProvider *renderedAircraftProvider = this->getRuntime()->getCContextNetwork();
606 IClientProvider *clientProvider = this->getRuntime()->getCContextNetwork();
608 factory->create(simulatorPluginInfo, ownAircraftProvider, renderedAircraftProvider, clientProvider);
609 Q_ASSERT_X(simulator, Q_FUNC_INFO, "no simulator driver can be created");
610
611 this->setRemoteAircraftProvider(renderedAircraftProvider);
612
613 // use simulator info from ISimulator as it can access the emulated driver settings
614 CSimulatorInfo simInfo = simulator->getSimulatorInfo();
615 if (!simInfo.isSingleSimulator())
616 {
617 SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Invalid simulator from plugin");
618 simInfo = CSimulatorInfo::p3d();
619 if (simulator->isEmulatedDriver())
620 {
621 CLogMessage(this).error(
622 u"Emulated driver does NOT provide valid simulator, using default '%1', plugin: '%2'")
623 << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
624 }
625 else
626 {
627 CLogMessage(this).error(u"Invalid driver using default '%1', plugin: '%2'")
628 << simInfo.toQString(true) << simulatorPluginInfo.toQString(true);
629 }
630 }
631 Q_ASSERT_X(simInfo.isSingleSimulator(), Q_FUNC_INFO, "need single simulator");
632
633 m_modelSetSimulator.set(simInfo);
634 const CAircraftModelList modelSetModels = this->getModelSet(); // synced
635 m_aircraftMatcher.setModelSet(modelSetModels, simInfo, true);
636 m_aircraftMatcher.setDefaultModel(simulator->getDefaultModel());
637
638 bool c =
639 connect(simulator, &ISimulator::simulatorStatusChanged, this, &CContextSimulator::onSimulatorStatusChanged);
640 Q_ASSERT(c);
642 &CContextSimulator::onAddingRemoteAircraftFailed);
643 Q_ASSERT(c);
645 &CContextSimulator::onOwnSimulatorModelChanged);
646 Q_ASSERT(c);
649 Q_ASSERT(c);
652 Q_ASSERT(c);
655 Q_ASSERT(c);
657 Q_ASSERT(c);
659 Q_ASSERT(c);
661 Q_ASSERT(c);
662
663 // disconnect for X-Plane FPS below 20
664 c = connect(simulator, &ISimulator::insufficientFrameRateDetected, this, [this](bool fatal) {
665 if (fatal)
666 {
667 emit this->vitalityLost();
668 }
669 });
670 Q_ASSERT(c);
673 Q_ASSERT(c);
674
675 // log from context to simulator
677 &CContextSimulator::relayStatusMessageToSimulator);
678 Q_ASSERT(c);
680 &CContextSimulator::relayStatusMessageToSimulator);
681 Q_ASSERT(c);
682 Q_UNUSED(c)
683
684 // Once the simulator signaled it is ready to simulate, add all known aircraft
685 m_initallyAddAircraft = true;
686 m_wasSimulating = false;
687 m_matchingMessages.clear();
688 m_failoverAddingCounts.clear();
689
690 // try to connect to simulator
691 const bool connected = simulator->connectTo();
692 if (!connected)
693 {
694 CLogMessage(this).error(u"Simulator plugin connection to simulator '%1' failed")
695 << simulatorPluginInfo.toQString(true);
696 return false;
697 }
698
699 // when everything is set up connected, update the current plugin info
700 m_simulatorPlugin.first = simulatorPluginInfo;
701 m_simulatorPlugin.second = simulator;
702 m_simulatorPlugin.second->setInterpolationSetupGlobal(m_renderSettings.get());
703
704 // Emit signal after this function completes completely decoupled
705 QPointer<CContextSimulator> myself(this);
706 QTimer::singleShot(25, this, [=, this] {
707 if (!myself || !sApp || sApp->isShuttingDown())
708 {
709 return;
710 }
711 if (m_simulatorPlugin.second)
712 {
713 CLogMessage(this).info(u"Simulator plugin loaded: '%1' connected: %2")
714 << simulatorPluginInfo.toQString(true) << boolToYesNo(connected);
715
716 // use the real driver as this will also work eith emulated driver
717 emit this->simulatorPluginChanged(m_simulatorPlugin.second->getSimulatorPluginInfo());
718 emit this->simulatorChanged(m_simulatorPlugin.second->getSimulatorInfo());
719 }
720 });
721
722 return true;
723 }
724
725 bool CContextSimulator::listenForSimulator(const CSimulatorPluginInfo &simulatorInfo)
726 {
727 Q_ASSERT(this->getIContextApplication());
729 Q_ASSERT(!simulatorInfo.isUnspecified());
730 Q_ASSERT(m_simulatorPlugin.first.isUnspecified());
731
732 if (!m_listenersThread.isRunning())
733 {
734 m_listenersThread.setObjectName("CContextSimulator: Thread for listener " + simulatorInfo.getIdentifier());
735 m_listenersThread.start(QThread::LowPriority);
736 }
737
738 ISimulatorListener *listener = m_plugins->createListener(simulatorInfo.getIdentifier());
739 if (!listener)
740 {
741 return false;
742 }
743
744 if (listener->thread() != &m_listenersThread)
745 {
746 Q_ASSERT_X(!listener->parent(), Q_FUNC_INFO, "Objects with parent cannot be moved to thread");
747
748 const bool c = connect(listener, &ISimulatorListener::simulatorStarted, this,
749 &CContextSimulator::onSimulatorStarted, Qt::QueuedConnection);
750 if (!c)
751 {
752 CLogMessage(this).error(u"Unable to use '%1'") << simulatorInfo.toQString();
753 return false;
754 }
755 listener->setProperty("isInitialized", true);
756 listener->moveToThread(&m_listenersThread);
757 }
758
759 // start if not already running
760 if (!listener->isRunning())
761 {
763 Q_ASSERT_X(s, Q_FUNC_INFO, "cannot invoke method");
764 Q_UNUSED(s)
765 }
766 CLogMessage(this).info(u"Listening for simulator '%1'") << simulatorInfo.getIdentifier();
767 return true;
768 }
769
770 void CContextSimulator::listenForAllSimulators()
771 {
772 const auto plugins = getAvailableSimulatorPlugins();
773 for (const CSimulatorPluginInfo &p : plugins)
774 {
775 Q_ASSERT(!p.isUnspecified());
776 if (p.isValid())
777 {
778 this->listenForSimulator(p);
779 }
780 }
781 }
782
783 void CContextSimulator::unloadSimulatorPlugin()
784 {
785 if (!m_simulatorPlugin.first.isUnspecified())
786 {
787 ISimulator *simulator = m_simulatorPlugin.second;
788 if (simulator && simulator->isConnected())
789 {
790 // we are about to unload an connected simulator
791 this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
793 }
794
795 m_simulatorPlugin.second = nullptr;
796 m_simulatorPlugin.first = CSimulatorPluginInfo();
797
798 Q_ASSERT(this->getIContextNetwork());
799 Q_ASSERT(this->getIContextNetwork()->isLocalObject());
800
801 // unload and disconnect
802 if (simulator)
803 {
804 // disconnect signals and delete
805 simulator->disconnect(this);
806 simulator->unload();
807 simulator->deleteLater();
808 emit this->simulatorPluginChanged(CSimulatorPluginInfo());
809 emit this->simulatorChanged(CSimulatorInfo());
810 }
811
812 if (m_wasSimulating)
813 {
814 emit this->vitalityLost();
815 }
816 m_wasSimulating = false;
817 }
818 }
819
820 void CContextSimulator::xCtxAddedRemoteAircraftReadyForModelMatching(const CSimulatedAircraft &remoteAircraft)
821 {
822 if (!this->isSimulatorPluginAvailable())
823 {
824 return;
825 }
826
827 const CCallsign callsign = remoteAircraft.getCallsign();
828 SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Remote aircraft with empty callsign");
829 if (callsign.isEmpty())
830 {
831 return;
832 }
833
834 // here we find the best simulator model for a resolved model
835 // in the first step we already tried to find accurate ICAO codes etc.
836 // coming from CAirspaceMonitor::sendReadyForModelMatching
837 MatchingLog whatToLog = m_logMatchingMessages;
838 CStatusMessageList matchingMessages;
839 CStatusMessageList *pMatchingMessages = m_logMatchingMessages > 0 ? &matchingMessages : nullptr;
840 CAircraftModel aircraftModel =
841 m_aircraftMatcher.getClosestMatch(remoteAircraft, whatToLog, pMatchingMessages, true);
842 Q_ASSERT_X(remoteAircraft.getCallsign() == aircraftModel.getCallsign(), Q_FUNC_INFO, "Mismatching callsigns");
843
844 // decide CG
845 const CLength cgModel = aircraftModel.getCG();
846 const CLength cgSim = m_simulatorPlugin.second->getSimulatorCGPerModelString(aircraftModel.getModelString());
847 const CSimulatorSettings simSettings = this->getSimulatorSettings();
848 switch (simSettings.getCGSource())
849 {
850 case CSimulatorSettings::CGFromSimulatorOnly: aircraftModel.setCG(cgSim); break;
851 case CSimulatorSettings::CGFromSimulatorFirst:
852 if (!cgSim.isNull())
853 {
854 aircraftModel.setCG(cgSim);
855 }
856 break;
857 case CSimulatorSettings::CGFromDBFirst:
858 if (cgModel.isNull())
859 {
860 aircraftModel.setCG(cgSim);
861 }
862 break;
863 // case CSimulatorSettings::CGFromDBOnly:
864 default: break; // leave CG from model alone
865 }
866
867 const CLength overriddenCG =
868 m_simulatorPlugin.second->overriddenCGorDefault(CLength::null(), aircraftModel.getModelString());
869 if (!overriddenCG.isNull())
870 {
871 aircraftModel.setCG(overriddenCG);
872 }
873
874 // model in provider
875 this->updateAircraftModel(callsign, aircraftModel, this->identifier());
876
877 const CSimulatedAircraft aircraftAfterModelApplied =
878 this->getAircraftInRangeForCallsign(remoteAircraft.getCallsign());
879 if (!aircraftAfterModelApplied.hasModelString())
880 {
881 if (!aircraftAfterModelApplied.hasCallsign())
882 {
883 return;
884 } // removed
885 if (this->isAircraftInRange(aircraftAfterModelApplied.getCallsign()))
886 {
887 return;
888 } // removed, but callsig, we did crosscheck
889
890 // callsign, but no model string
891 CLogMessage(this).error(u"Matching error for '%1', no model string")
892 << aircraftAfterModelApplied.getCallsign().asString();
893
894 CSimulatedAircraft brokenAircraft(aircraftAfterModelApplied);
895 brokenAircraft.setEnabled(false);
896 brokenAircraft.setRendered(false);
898 pMatchingMessages, callsign,
899 QStringLiteral("Cannot add remote aircraft, no model string: '%1'").arg(brokenAircraft.toQString()));
900 emit this->aircraftRenderingChanged(brokenAircraft);
901 return;
902 }
903
904 // here the model is added to the simulator
905 m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraftAfterModelApplied);
907 pMatchingMessages, callsign,
908 QStringLiteral("Logically added remote aircraft: %1").arg(aircraftAfterModelApplied.toQString()));
909
910 this->clearMatchingMessages(callsign);
911 this->addMatchingMessages(callsign, matchingMessages);
912
913 // done
914 emit this->modelMatchingCompleted(aircraftAfterModelApplied);
915 }
916
917 void CContextSimulator::xCtxRemovedRemoteAircraft(const CCallsign &callsign)
918 {
919 if (!this->isSimulatorAvailable())
920 {
921 return;
922 }
923 m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
924 m_failoverAddingCounts.remove(callsign);
925 }
926
927 void CContextSimulator::onSimulatorStatusChanged(ISimulator::SimulatorStatus status)
928 {
929 m_wasSimulating = m_wasSimulating || status.testFlag(ISimulator::Simulating);
930 if (m_initallyAddAircraft && status.testFlag(ISimulator::Simulating))
931 {
932 // use network to initally add aircraft
933 IContextNetwork *networkContext = this->getIContextNetwork();
934 Q_ASSERT_X(networkContext, Q_FUNC_INFO, "Need context");
935 Q_ASSERT_X(networkContext->isLocalObject(), Q_FUNC_INFO, "Need local object");
936
937 // initially add aircraft
938 const CSimulatedAircraftList aircraft = networkContext->getAircraftInRange();
939 for (const CSimulatedAircraft &simulatedAircraft : aircraft)
940 {
941 SWIFT_VERIFY_X(!simulatedAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "Need callsign");
942 this->xCtxAddedRemoteAircraftReadyForModelMatching(simulatedAircraft);
943 }
944 m_initallyAddAircraft = false;
945 }
946
947 if (!status.testFlag(ISimulator::Connected))
948 {
949 // we got disconnected, plugin no longer needed
950 this->updateMarkAllAsNotRendered(); // without plugin nothing can be rendered
951 this->unloadSimulatorPlugin();
952 this->restoreSimulatorPlugins();
953
954 if (m_wasSimulating)
955 {
956 emit this->vitalityLost();
957 }
958 m_wasSimulating = false;
959 }
960
961 emit this->simulatorStatusChanged(status);
962 }
963
964 void CContextSimulator::xCtxTextMessagesReceived(const network::CTextMessageList &textMessages)
965 {
966 if (!this->isSimulatorAvailable())
967 {
968 return;
969 }
970 if (!this->getIContextOwnAircraft())
971 {
972 return;
973 }
974
975 const CSimulatorMessagesSettings settings = m_messageSettings.getThreadLocal();
976 const CSimulatedAircraft ownAircraft = this->getIContextOwnAircraft()->getOwnAircraft();
977 for (const auto &tm : textMessages)
978 {
979 if (!settings.relayThisTextMessage(tm, ownAircraft))
980 {
981 continue;
982 }
983 m_simulatorPlugin.second->displayTextMessage(tm);
984 }
985 }
986
987 void CContextSimulator::xCtxChangedRemoteAircraftModel(const CSimulatedAircraft &aircraft,
988 const swift::misc::CIdentifier &originator)
989 {
990 if (CIdentifiable::isMyIdentifier(originator))
991 {
992 return;
993 }
994 if (!this->isSimulatorAvailable())
995 {
996 return;
997 }
998 m_simulatorPlugin.second->changeRemoteAircraftModel(aircraft);
999 }
1000
1001 void CContextSimulator::xCtxChangedOwnAircraftModel(const CAircraftModel &aircraftModel,
1002 const CIdentifier &originator)
1003 {
1004 if (CIdentifiable::isMyIdentifier(originator))
1005 {
1006 return;
1007 }
1008 if (!this->isSimulatorAvailable())
1009 {
1010 return;
1011 }
1012
1013 emit this->ownAircraftModelChanged(aircraftModel);
1014 }
1015
1016 void CContextSimulator::xCtxChangedRemoteAircraftEnabled(const CSimulatedAircraft &aircraft)
1017 {
1018 if (!this->isSimulatorAvailable())
1019 {
1020 return;
1021 }
1022 m_simulatorPlugin.second->changeRemoteAircraftEnabled(aircraft);
1023 }
1024
1025 void CContextSimulator::xCtxNetworkConnectionStatusChanged(const CConnectionStatus &from,
1026 const CConnectionStatus &to)
1027 {
1028 Q_UNUSED(from)
1029 SWIFT_VERIFY_X(this->getIContextNetwork(), Q_FUNC_INFO, "Missing network context");
1030 SWIFT_VERIFY_X(this->getIContextOwnAircraft(), Q_FUNC_INFO, "Missing own aircraft context");
1031 if (to.isConnected() && this->getIContextNetwork())
1032 {
1033 m_networkSessionId = this->getIContextNetwork()->getConnectedServer().getServerSessionId(false);
1034 if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
1035 {
1036 m_simulatorPlugin.second->setFlightNetworkConnected(true);
1037 m_simulatorPlugin.second->setOwnCallsign(
1038 this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
1039 }
1040 }
1041 else if (to.isDisconnected())
1042 {
1043 m_networkSessionId.clear();
1044 m_aircraftMatcher.clearMatchingStatistics();
1045 m_matchingMessages.clear();
1046 m_failoverAddingCounts.clear();
1047
1048 if (m_simulatorPlugin.second) // check in case the plugin has been unloaded
1049 {
1050 m_simulatorPlugin.second->removeAllRemoteAircraft(); // also removes aircraft
1051 m_simulatorPlugin.second->setFlightNetworkConnected(false);
1052 m_simulatorPlugin.second->setOwnCallsign({});
1053 }
1054 }
1055 }
1056
1057 void CContextSimulator::onAddingRemoteAircraftFailed(const CSimulatedAircraft &remoteAircraft, bool disabled,
1058 bool requestFailover, const CStatusMessage &message)
1059 {
1060 if (!this->isSimulatorAvailable())
1061 {
1062 return;
1063 }
1064 if (disabled)
1065 {
1066 m_aircraftMatcher.addingRemoteModelFailed(remoteAircraft);
1067 }
1068
1069 const CCallsign cs = remoteAircraft.getCallsign();
1070 if (cs.isEmpty())
1071 {
1072 return;
1073 }
1074
1075 bool failover = requestFailover;
1076 if (requestFailover)
1077 {
1078 const CAircraftMatcherSetup setup = m_aircraftMatcher.getSetup();
1079 if (setup.doModelAddFailover())
1080 {
1081 const int trial = m_failoverAddingCounts.value(cs, 0);
1082 if (trial < MaxModelAddedFailoverTrials)
1083 {
1084 m_failoverAddingCounts[cs] = trial + 1;
1085 this->doMatchingAgain(cs); // has its own single shot logic
1086 }
1087 else
1088 {
1089 failover = false;
1090 CLogMessage(this).warning(u"Model for '%1' failed adding, tried %2 time(s) to resolve, giving up")
1091 << cs.toQString(true) << (trial + 1);
1092 }
1093 }
1094 }
1095 emit this->addingRemoteModelFailed(remoteAircraft, disabled, failover, message);
1096 }
1097
1098 void CContextSimulator::xCtxUpdateSimulatorCockpitFromContext(const CSimulatedAircraft &ownAircraft,
1099 const CIdentifier &originator)
1100 {
1101 if (!this->isSimulatorAvailable())
1102 {
1103 return;
1104 }
1105 if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName())
1106 {
1107 return;
1108 }
1109
1110 // update
1111 m_simulatorPlugin.second->updateOwnSimulatorCockpit(ownAircraft, originator);
1112 }
1113
1114 void CContextSimulator::xCtxUpdateSimulatorSelcalFromContext(const CSelcal &selcal, const CIdentifier &originator)
1115 {
1116 if (!this->isSimulatorAvailable())
1117 {
1118 return;
1119 }
1120 if (originator.getName().isEmpty() || originator == IContextSimulator::InterfaceName())
1121 {
1122 return;
1123 }
1124
1125 // update
1126 m_simulatorPlugin.second->updateOwnSimulatorSelcal(selcal, originator);
1127 }
1128
1129 void CContextSimulator::xCtxNetworkRequestedNewAircraft(const CCallsign &callsign, const QString &aircraftIcao,
1130 const QString &airlineIcao, const QString &livery)
1131 {
1132 if (m_networkSessionId.isEmpty())
1133 {
1134 return;
1135 }
1136 m_aircraftMatcher.evaluateStatisticsEntry(m_networkSessionId, callsign, aircraftIcao, airlineIcao, livery);
1137 }
1138
1139 void CContextSimulator::relayStatusMessageToSimulator(const swift::misc::CStatusMessage &message)
1140 {
1141 if (!this->isSimulatorAvailable())
1142 {
1143 return;
1144 }
1145 if (!sApp || sApp->isShuttingDown())
1146 {
1147 return;
1148 }
1149 const CSimulatorMessagesSettings simMsg = m_messageSettings.getThreadLocal();
1150 if (simMsg.relayThisStatusMessage(message) && m_simulatorPlugin.second)
1151 {
1152 m_simulatorPlugin.second->displayStatusMessage(message);
1153 }
1154 }
1155
1156 void CContextSimulator::changeEnabledSimulators()
1157 {
1158 CSimulatorPluginInfo currentPluginInfo = m_simulatorPlugin.first;
1159 const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
1160
1161 // Unload the current plugin, if it is no longer enabled
1162 if (!currentPluginInfo.isUnspecified() && !enabledSimulators.contains(currentPluginInfo.getIdentifier()))
1163 {
1164 unloadSimulatorPlugin();
1166 }
1167 restoreSimulatorPlugins();
1168 }
1169
1170 void CContextSimulator::restoreSimulatorPlugins()
1171 {
1172 if (!m_simulatorPlugin.first.isUnspecified())
1173 {
1174 return;
1175 }
1176
1177 this->stopSimulatorListeners();
1178 const QStringList enabledSimulators = m_enabledSimulators.getThreadLocal();
1179 const CSimulatorPluginInfoList allSimulators = m_plugins->getAvailableSimulatorPlugins();
1180 for (const CSimulatorPluginInfo &s : allSimulators)
1181 {
1182 if (enabledSimulators.contains(s.getIdentifier()))
1183 {
1185 }
1186 }
1187 }
1188
1190 {
1191 if (isDebugEnabled())
1192 {
1193 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign;
1194 }
1195 return m_matchingMessages[callsign];
1196 }
1197
1199 {
1200 if (isDebugEnabled())
1201 {
1202 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
1203 }
1204 return m_logMatchingMessages;
1205 }
1206
1208 {
1209 if (isDebugEnabled())
1210 {
1211 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << matchingLogToString(enabled);
1212 }
1213 if (m_logMatchingMessages == enabled)
1214 {
1215 return;
1216 }
1217 m_logMatchingMessages = enabled;
1219 }
1220
1222 {
1223 if (isDebugEnabled())
1224 {
1225 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << missingOnly;
1226 }
1227 const CMatchingStatistics statistics = m_aircraftMatcher.getCurrentStatistics();
1228 return missingOnly ? statistics.findMissingOnly() : statistics;
1229 }
1230
1232 {
1233 if (isDebugEnabled())
1234 {
1235 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << setup.toQString();
1236 }
1237 m_aircraftMatcher.setSetup(setup);
1238 const CStatusMessage msg = m_matchingSettings.setAndSave(setup);
1240 }
1241
1243 {
1244 if (isDebugEnabled())
1245 {
1246 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
1247 }
1248 return m_aircraftMatcher.getSetup();
1249 }
1250
1252 {
1253 if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected())
1254 {
1255 return false;
1256 }
1257 bool added = add;
1258 if (add)
1259 {
1260 m_simulatorPlugin.second->setTestMode(true);
1261 added = m_simulatorPlugin.second->logicallyAddRemoteAircraft(aircraft);
1262 }
1263 else
1264 {
1265 m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(aircraft.getCallsign());
1266 m_simulatorPlugin.second->setTestMode(false); // AFTER we have removed it
1267 }
1268 return added;
1269 }
1270
1272 const CAircraftParts &parts)
1273 {
1274 if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected())
1275 {
1276 return false;
1277 }
1278 CAircraftSituation s = situation; // make sure to have correct callsign
1279 s.setCallsign(cs);
1280 return m_simulatorPlugin.second->testSendSituationAndParts(cs, s, parts);
1281 }
1282
1283 bool CContextSimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
1284 {
1285 Q_UNUSED(originator)
1286 if (commandLine.isEmpty())
1287 {
1288 return false;
1289 }
1290 CSimpleCommandParser parser({
1291 ".plugin", ".drv", ".driver", // forwarded to driver
1292 ".ris" // rendering interpolator setup
1293 });
1294 parser.parse(commandLine);
1295 if (!parser.isKnownCommand())
1296 {
1297 return false;
1298 }
1299 if (parser.matchesCommand("ris"))
1300 {
1302 const QString p1 = parser.part(1);
1303 if (!parser.hasPart(2))
1304 {
1305 return false;
1306 }
1307 const bool on = stringToBool(parser.part(2));
1308 if (p1 == "debug")
1309 {
1311 }
1312 else if (p1 == "parts")
1313 {
1315 }
1316 else
1317 {
1318 return false;
1319 }
1321 CLogMessage(this, CLogCategories::cmdLine()).info(u"Setup is: '%1'") << rs.toQString(true);
1322 return true;
1323 }
1324 if (parser.matchesCommand("plugin") || parser.matchesCommand("drv") || parser.matchesCommand("driver"))
1325 {
1326 if (!m_simulatorPlugin.second)
1327 {
1328 return false;
1329 }
1330 return m_simulatorPlugin.second->parseCommandLine(commandLine, originator);
1331 }
1332 return false;
1333 }
1334
1336 {
1337 if (!this->isSimulatorAvailable() || !m_simulatorPlugin.second)
1338 {
1339 return nullptr;
1340 }
1341 return m_simulatorPlugin.second;
1342 }
1343
1345 {
1346 if (isDebugEnabled())
1347 {
1348 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign;
1349 }
1350 if (!m_simulatorPlugin.second)
1351 {
1352 return false;
1353 }
1354 return m_simulatorPlugin.second->followAircraft(callsign);
1355 }
1356
1358 {
1359 if (isDebugEnabled())
1360 {
1361 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
1362 }
1363 if (!m_simulatorPlugin.second)
1364 {
1365 return;
1366 }
1367 m_simulatorPlugin.second->recalculateAllAircraft();
1368 }
1369
1371 {
1372 CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1373 if (aircraft.getCallsign() != callsign)
1374 {
1375 return false;
1376 } // not found
1377 if (!this->isSimulatorAvailable())
1378 {
1379 return false;
1380 }
1381
1382 m_simulatorPlugin.second->logicallyRemoveRemoteAircraft(callsign);
1383 aircraft.setModel(aircraft.getNetworkModel()); // like originally from network
1384 QPointer<CContextSimulator> myself(this);
1385 QTimer::singleShot(1000, this, [=, this] {
1386 if (!sApp || sApp->isShuttingDown() || !myself)
1387 {
1388 return;
1389 }
1390 this->xCtxAddedRemoteAircraftReadyForModelMatching(aircraft);
1391 });
1392 return true;
1393 }
1394
1396 {
1397 if (isDebugEnabled())
1398 {
1399 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << situation;
1400 }
1401 if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected())
1402 {
1403 return false;
1404 }
1405 return m_simulatorPlugin.second->requestElevationBySituation(situation);
1406 }
1407
1409 const CLength &range) const
1410 {
1411 if (isDebugEnabled())
1412 {
1414 << Q_FUNC_INFO << reference.convertToQString(true) << range;
1415 }
1416 if (!m_simulatorPlugin.second || !m_simulatorPlugin.second->isConnected())
1417 {
1418 return CElevationPlane::null();
1419 }
1420 return m_simulatorPlugin.second->findClosestElevationWithinRange(reference, range);
1421 }
1422
1424 {
1425 if (isDebugEnabled())
1426 {
1427 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO;
1428 }
1429 const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
1430 if (callsigns.isEmpty())
1431 {
1432 return 0;
1433 }
1434 int delayMs = 25;
1435 QPointer<CContextSimulator> myself(this);
1436 for (const CCallsign &cs : callsigns)
1437 {
1438 QTimer::singleShot(delayMs, this, [=, this] {
1439 if (!sApp || sApp->isShuttingDown() || !myself)
1440 {
1441 return;
1442 }
1443 this->doMatchingAgain(cs);
1444 });
1445 delayMs += 25;
1446 }
1447 return callsigns.size();
1448 }
1449
1451 {
1452 if (isDebugEnabled())
1453 {
1454 CLogMessage(this, CLogCategories::contextSlot()).debug() << Q_FUNC_INFO << callsign.asString();
1455 }
1456 if (!this->isAircraftInRange(callsign))
1457 {
1458 return false;
1459 }
1460 if (!this->isSimulatorAvailable())
1461 {
1462 return false;
1463 }
1464
1465 QPointer<CContextSimulator> myself(this);
1466 QTimer::singleShot(2500, this, [=, this] {
1467 if (!sApp || sApp->isShuttingDown() || !myself)
1468 {
1469 return;
1470 }
1471 const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1472 if (!aircraft.hasCallsign())
1473 {
1474 return;
1475 } // no longer valid
1476 CSimulatedAircraft resetAircraft(aircraft);
1477 resetAircraft.resetToNetworkModel();
1478 resetAircraft.setEnabled(true);
1479 this->xCtxAddedRemoteAircraftReadyForModelMatching(resetAircraft);
1480 });
1481 return true;
1482 }
1483
1484 void CContextSimulator::onSimulatorStarted(const CSimulatorPluginInfo &info)
1485 {
1486 this->stopSimulatorListeners();
1487 this->loadSimulatorPlugin(info);
1488
1489 // if we have enabled messages, we will disable if size getting too high
1490 if (m_logMatchingMessages)
1491 {
1492 const QPointer<CContextSimulator> myself(this);
1493 QTimer::singleShot(5000, this, [=, this] {
1494 if (!myself)
1495 {
1496 return;
1497 }
1498 if (m_aircraftMatcher.getModelSetCount() > MatchingLogMaxModelSetSize)
1499 {
1500 const MatchingLog log = CBuildConfig::isDebugBuild() ? MatchingLogAll : MatchingLogSimplified;
1501 this->enableMatchingMessages(log);
1502 }
1503 });
1504 }
1505 }
1506
1507 void CContextSimulator::onOwnSimulatorModelChanged(const CAircraftModel &model)
1508 {
1509 CAircraftModel lookupModel(model);
1510 if (!lookupModel.isLoadedFromDb())
1511 {
1512 lookupModel = this->reverseLookupModel(model);
1513 }
1514 emit this->ownAircraftModelChanged(lookupModel);
1515 }
1516
1517 void CContextSimulator::stopSimulatorListeners()
1518 {
1519 for (const CSimulatorPluginInfo &info : getAvailableSimulatorPlugins())
1520 {
1521 ISimulatorListener *listener = m_plugins->getListener(info.getIdentifier());
1522 if (listener)
1523 {
1524 const bool s = QMetaObject::invokeMethod(listener, &ISimulatorListener::stop);
1525 Q_ASSERT_X(s, Q_FUNC_INFO, "Cannot invoke stop");
1526 Q_UNUSED(s)
1527 }
1528 }
1529 }
1530
1531 void CContextSimulator::addMatchingMessages(const CCallsign &callsign, const CStatusMessageList &messages)
1532 {
1533 if (callsign.isEmpty())
1534 {
1535 return;
1536 }
1537 if (messages.isEmpty())
1538 {
1539 return;
1540 }
1541 if (!m_logMatchingMessages)
1542 {
1543 return;
1544 }
1545 if (m_matchingMessages.contains(callsign))
1546 {
1547 CStatusMessageList &msgs = m_matchingMessages[callsign];
1548 msgs.push_back(messages);
1549 }
1550 else
1551 {
1552 m_matchingMessages.insert(callsign, messages);
1553 }
1554 }
1555
1556 void CContextSimulator::clearMatchingMessages(const CCallsign &callsign)
1557 {
1558 if (callsign.isEmpty())
1559 {
1560 return;
1561 }
1562 m_matchingMessages.remove(callsign);
1563 }
1564
1565 CAircraftModel CContextSimulator::reverseLookupModel(const CAircraftModel &model)
1566 {
1567 bool modified = false;
1568 const CAircraftModel reverseModel =
1570 return reverseModel;
1571 }
1572
1573 void CContextSimulator::initByLastUsedModelSet()
1574 {
1575 // no models in matcher, but in cache, we can set them as default
1576 const CSimulatorInfo simulator(m_modelSetSimulator.get());
1578 const CAircraftModelList models(this->getModelSet()); // synced
1579 CLogMessage(this).info(u"Init aircraft matcher with %1 models from set for '%2'")
1580 << models.size() << simulator.toQString();
1581 m_aircraftMatcher.setModelSet(models, simulator, false);
1582 }
1583} // namespace swift::core::context
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
static constexpr bool isDebugBuild()
Debug build?
static bool isLocalDeveloperDebugBuild()
Local build for developers.
void setupChanged()
Setup changed.
int getModelSetCount() const
Model set count.
bool isShuttingDown() const
Is application shutting down?
ContextMode
How to handle a given context.
context::CContextNetwork * getCContextNetwork()
Context for network.
context::CContextOwnAircraft * getCContextOwnAircraft()
Context for own aircraft.
Manages plugins for the simulator context.
ISimulatorFactory * getFactory(const QString &pluginId)
Get simulator factory from the plugin.
Factory pattern class to create instances of ISimulator.
Definition simulator.h:694
virtual ISimulator * create(const swift::misc::simulation::CSimulatorPluginInfo &info, swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider, swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, swift::misc::network::IClientProvider *clientProvider)=0
Create a new instance of a driver.
Interface to a simulator.
Definition simulator.h:59
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.
void stop()
Stops listening.
bool isRunning() const
Running?
Definition simulator.h:647
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.
CContextSimulator(CCoreFacadeConfig::ContextMode, CCoreFacade *runtime)
Constructor.
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.
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:62
CCoreFacade * getRuntime()
Runtime.
Definition context.h:57
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.
static swift::misc::simulation::CAircraftModel consolidateOwnAircraftModelWithDbData(const swift::misc::simulation::CAircraftModel &model, bool force, bool *modified=nullptr)
Consolidate own (aircraft) model data with DB data.
size_type size() const
Returns number of elements in the collection.
Definition collection.h:191
bool isEmpty() const
Synonym for empty.
Definition collection.h:197
bool isMyIdentifier(const CIdentifier &otherIdentifier) const
My identifier?
CIdentifiable(const QString &objectName)
Use literal based object name.
const CIdentifier & identifier() const
Get identifier.
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.
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 & debug()
Set the severity to debug.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
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:308
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?
Value object encapsulating information of aircraft's parts.
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
static void addLogDetailsToList(CStatusMessageList *log, const CCallsign &callsign, const QString &message, const QStringList &extraCategories={}, CStatusMessage::StatusSeverity s=CStatusMessage::SeverityInfo)
Specialized log for matching / reverse lookup.
Definition callsign.cpp:155
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
QString convertToQString(bool i18n=false) const
Cast as QString.
Plane of same elevation, can be a single point or larger area (e.g. airport).
static const CElevationPlane & null()
NULL plane.
QString toQString(bool i18n=false) const
Cast as QString.
Definition mixinstring.h:74
bool isDisconnected() const
Query status.
QString getServerSessionId(bool onlyConnected) const
Identifying a session, if not connected empty.
Definition server.cpp:106
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.
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
const physical_quantities::CLength & getCG() const
Get center of gravity.
void setCG(const physical_quantities::CLength &cg)
Get center of gravity.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
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.
void setCurrentSimulator(const CSimulatorInfo &simulator, const QString &simDirectory, const QStringList &modelDirList)
Corresponding simulator.
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?
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 aviation::CCallsign & getCallsign() const
Get callsign.
bool hasCallsign() const
Callsign not empty, no further checks.
bool setEnabled(bool enabled)
Enabled / disabled.
const simulation::CAircraftModel & getNetworkModel() const
Get network model.
bool resetToNetworkModel()
Reset to the newtork model.
Simple hardcoded info about the corresponding simulator.
static const CSimulatorInfo & p3d()
Const simulator info objects.
bool isSingleSimulator() const
Single simulator selected.
bool isNoSimulator() const
No simulator?
Simulator internals for flight simulators. Those are obtained from a running simulator and represent ...
const CSimulatorInfo & getSimulatorInfo() const
Simulator info object.
bool isUnspecified() const
Unspecified simulator.
const QString & getIdentifier() const
Identifier.
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.
CAircraftModelList getCachedModels(const CSimulatorInfo &simulator) const
Models for simulator.
void synchronizeCache(const CSimulatorInfo &simulator)
Synchronize for given simulator.
CStatusMessage setCachedModels(const CAircraftModelList &models, const CSimulatorInfo &simulator)
Set cached models.
static CCentralMultiSimulatorModelSetCachesProvider & modelCachesInstance()
Central instance.
CSimulatorInfo simulatorsWithInitializedCache() const
Initialized caches for which simulator?
void cacheChanged(const swift::misc::simulation::CSimulatorInfo &simulator)
Cache has been changed.
QStringList getModelDirectoriesOrDefault(const CSimulatorInfo &simulator) const
Model directory or default model path per simulator.
QString getSimulatorDirectoryOrDefault(const CSimulatorInfo &simulator) const
Simulator directory or default model path per simulator.
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)
Classes interacting with the swift database (aka "datastore").
Free functions in swift::misc.
SWIFT_MISC_EXPORT const QString & boolToYesNo(bool v)
Bool to yes/no.
SWIFT_MISC_EXPORT bool stringToBool(const QString &boolString)
Convert string to bool.
bool invokeMethod(QObject *context, Functor &&function, Args &&... arguments)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
bool moveToThread(QThread *targetThread)
QObject * parent() const const
void setObjectName(QAnyStringView name)
bool setProperty(const char *name, QVariant &&value)
QThread * thread() const const
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
CaseInsensitive
QueuedConnection
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition verify.h:26