swift
simulator.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 
4 #include "core/simulator.h"
5 
6 #include <functional>
7 
8 #include <QDateTime>
9 #include <QDesktopServices>
10 #include <QDir>
11 #include <QFlag>
12 #include <QPointer>
13 #include <QString>
14 #include <QStringBuilder>
15 #include <QThread>
16 #include <QUrl>
17 #include <Qt>
18 #include <QtGlobal>
19 
20 #include "core/application.h"
21 #include "core/db/databaseutils.h"
22 #include "core/webdataservices.h"
23 #include "misc/crashhandler.h"
24 #include "misc/directoryutils.h"
25 #include "misc/logmessage.h"
26 #include "misc/math/mathutils.h"
28 #include "misc/threadutils.h"
29 #include "misc/verify.h"
30 
31 using namespace swift::config;
32 using namespace swift::misc;
33 using namespace swift::misc::aviation;
34 using namespace swift::misc::geo;
35 using namespace swift::misc::math;
36 using namespace swift::misc::simulation;
37 using namespace swift::misc::simulation::data;
38 using namespace swift::misc::simulation::settings;
39 using namespace swift::misc::physical_quantities;
40 using namespace swift::misc::network;
41 using namespace swift::misc::weather;
42 using namespace swift::core::db;
43 
44 namespace swift::core
45 {
46  const QStringList &ISimulator::getLogCategories()
47  {
48  static const QStringList cats({ CLogCategories::driver(), CLogCategories::plugin() });
49  return cats;
50  }
51 
52  ISimulator::~ISimulator() { this->safeKillTimer(); }
53 
54  ISimulator::SimulatorStatus ISimulator::getSimulatorStatus() const
55  {
56  if (!this->isConnected()) { return ISimulator::Disconnected; }
57  const SimulatorStatus status =
58  Connected |
59  (this->isSimulating() ? ISimulator::Simulating : static_cast<ISimulator::SimulatorStatusFlag>(0)) |
60  (this->isPaused() ? ISimulator::Paused : static_cast<ISimulator::SimulatorStatusFlag>(0));
61  return status;
62  }
63 
64  bool ISimulator::logicallyRemoveRemoteAircraft(const CCallsign &callsign)
65  {
66  // if not restricted, directly change
67  if (!this->getInterpolationSetupGlobal().isRenderingRestricted())
68  {
69  m_statsPhysicallyAddedAircraft++;
70  this->callPhysicallyRemoveRemoteAircraft(callsign);
71  return true;
72  }
73 
74  // will be removed with next snapshot onRecalculatedRenderedAircraft
75  return false;
76  }
77 
78  bool ISimulator::logicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft)
79  {
80  if (!this->validateModelOfAircraft(remoteAircraft))
81  {
82  const CCallsign cs = remoteAircraft.getCallsign();
83  CLogMessage(this).warning(u"Invalid aircraft detected, which will be disabled: '%1' '%2'")
84  << cs << remoteAircraft.getModelString();
85  this->updateAircraftEnabled(cs, false);
86  this->updateAircraftRendered(cs, false);
87  return false;
88  }
89 
90  // no invalid model should ever reach this place here
91  const bool renderingRestricted = this->getInterpolationSetupGlobal().isRenderingRestricted();
92  if (this->showDebugLogMessage())
93  {
94  this->debugLogMessage(Q_FUNC_INFO,
95  QStringLiteral("Restricted: %1 cs: '%2' enabled: %3")
96  .arg(boolToYesNo(renderingRestricted), remoteAircraft.getCallsignAsString(),
97  boolToYesNo(remoteAircraft.isEnabled())));
98  }
99  if (!remoteAircraft.isEnabled()) { return false; }
100 
101  // if not restricted, directly change
102  if (!renderingRestricted)
103  {
104  this->callPhysicallyAddRemoteAircraft(remoteAircraft);
105  return true;
106  }
107 
108  // restricted -> will be added with next snapshot onRecalculatedRenderedAircraft
109  return false;
110  }
111 
112  bool ISimulator::followAircraft(const CCallsign &callsign)
113  {
114  Q_UNUSED(callsign)
115  return false;
116  }
117 
118  void ISimulator::recalculateAllAircraft() { this->setUpdateAllRemoteAircraft(); }
119 
120  void ISimulator::setFlightNetworkConnected(bool connected) { m_networkConnected = connected; }
121 
122  void ISimulator::setOwnCallsign(const CCallsign &callsign)
123  {
124  Q_UNUSED(callsign)
125  // void
126  }
127 
128  void ISimulator::clearAllRemoteAircraftData()
129  {
130  // rendering related stuff
131  m_addAgainAircraftWhenRemoved.clear();
132  m_callsignsToBeRendered.clear();
133  this->resetLastSentValues(); // clear all last sent values
134  m_updateRemoteAircraftInProgress = false;
135 
136  this->clearInterpolationSetupsPerCallsign();
137  this->resetAircraftStatistics();
138  }
139 
140  void ISimulator::debugLogMessage(const QString &msg)
141  {
142  if (!this->showDebugLogMessage()) { return; }
143  if (msg.isEmpty()) { return; }
144  const CStatusMessage m = CStatusMessage(this).info(u"%1") << msg;
145  emit this->driverMessages(m);
146  }
147 
148  void ISimulator::debugLogMessage(const QString &funcInfo, const QString &msg)
149  {
150  if (!this->showDebugLogMessage()) { return; }
151  if (msg.isEmpty()) { return; }
152  const CStatusMessage m = CStatusMessage(this).info(u"%1 %2") << msg << funcInfo;
153  emit this->driverMessages(m);
154  }
155 
156  bool ISimulator::showDebugLogMessage() const
157  {
158  const bool show = this->getInterpolationSetupGlobal().showSimulatorDebugMessages();
159  return show;
160  }
161 
162  void ISimulator::resetAircraftFromProvider(const CCallsign &callsign)
163  {
164  const CSimulatedAircraft aircraft(this->getAircraftInRangeForCallsign(callsign));
165  const bool enabled = aircraft.isEnabled();
166  if (enabled)
167  {
168  // are we already visible?
169  if (!this->isPhysicallyRenderedAircraft(callsign))
170  {
171  this->callPhysicallyAddRemoteAircraft(aircraft); // enable/disable
172  }
173  }
174  else { this->callPhysicallyRemoveRemoteAircraft(callsign); }
175  }
176 
177  void ISimulator::clearData(const CCallsign &callsign)
178  {
179  m_statsPhysicallyRemovedAircraft++;
180  m_lastSentParts.remove(callsign);
181  m_lastSentSituations.remove(callsign);
182  m_loopbackSituations.clear();
183  this->removeInterpolationSetupPerCallsign(callsign);
184  }
185 
186  bool ISimulator::addLoopbackSituation(const CAircraftSituation &situation)
187  {
188  const CCallsign cs = situation.getCallsign();
189  if (!this->isLogCallsign(cs)) { return false; }
190  CAircraftSituationList &situations = m_loopbackSituations[cs];
191  situations.push_frontKeepLatestAdjustedFirst(situation, true, 10);
192  return true;
193  }
194 
195  bool ISimulator::addLoopbackSituation(const CCallsign &callsign, const CElevationPlane &elevationPlane,
196  const CLength &cg)
197  {
198  if (!this->isLogCallsign(callsign)) { return false; }
199  CAircraftSituation situation(callsign, elevationPlane);
200  situation.setGroundElevation(elevationPlane, CAircraftSituation::FromProvider);
201  situation.setCG(cg);
202  situation.setCurrentUtcTime();
203  situation.setTimeOffsetMs(0);
204  CAircraftSituationList &situations = m_loopbackSituations[callsign];
205  situations.push_frontKeepLatestAdjustedFirst(situation, true, 10);
206  return true;
207  }
208 
209  void ISimulator::reset()
210  {
211  this->clearAllRemoteAircraftData(); // reset
212  m_averageFps = -1.0;
213  m_simTimeRatio = 1.0;
214  m_trackMilesShort = 0.0;
215  m_minutesLate = 0.0;
216  }
217 
218  bool ISimulator::isUpdateAllRemoteAircraft(qint64 currentTimestamp) const
219  {
220  if (m_updateAllRemoteAircraftUntil < 1) { return false; }
221  if (currentTimestamp < 0) { currentTimestamp = QDateTime::currentMSecsSinceEpoch(); }
222  return (m_updateAllRemoteAircraftUntil > currentTimestamp);
223  }
224 
225  void ISimulator::setUpdateAllRemoteAircraft(qint64 currentTimestamp, qint64 forMs)
226  {
227  if (currentTimestamp < 0) { currentTimestamp = QDateTime::currentMSecsSinceEpoch(); }
228  if (forMs < 0) { forMs = 10 * 1000; }
229  m_updateAllRemoteAircraftUntil = currentTimestamp + forMs;
230  this->resetLastSentValues();
231  }
232 
233  void ISimulator::resetUpdateAllRemoteAircraft() { m_updateAllRemoteAircraftUntil = -1; }
234 
235  void ISimulator::safeKillTimer()
236  {
237  if (m_timerId < 0) { return; }
238  SWIFT_AUDIT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Try to kill timer from another thread");
239  this->killTimer(m_timerId);
240  m_timerId = -1;
241  }
242 
243  CInterpolationAndRenderingSetupPerCallsign ISimulator::getInterpolationSetupConsolidated(const CCallsign &callsign,
244  bool forceFullUpdate) const
245  {
246  CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupPerCallsignOrDefault(callsign);
247  const CClient client = this->getClientOrDefaultForCallsign(callsign);
248  setup.consolidateWithClient(client);
249  if (forceFullUpdate) { setup.setForceFullInterpolation(forceFullUpdate); }
250  return setup;
251  }
252 
253  bool ISimulator::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &callsign)
254  {
255  Q_UNUSED(reference)
256  Q_UNUSED(callsign)
257  return false;
258  }
259 
260  void ISimulator::callbackReceivedRequestedElevation(const CElevationPlane &plane, const CCallsign &callsign,
261  bool isWater)
262  {
263  if (this->isShuttingDown()) { return; }
264  if (plane.isNull()) { return; } // this happens if requested for a coordinate where scenery is not available
265 
266  // Update in remote aircraft for given callsign
267  // this will trigger also a position update, new interpolant etc.
268  bool updatedForOnGroundPosition = false;
269  const int updated = CRemoteAircraftAware::updateAircraftGroundElevation(
270  callsign, plane, CAircraftSituation::FromProvider, &updatedForOnGroundPosition);
271 
272  // update in simulator and cache
273  const bool likelyOnGroundElevation = updated > 0 && updatedForOnGroundPosition;
274  ISimulationEnvironmentProvider::rememberGroundElevation(callsign, likelyOnGroundElevation,
275  plane); // in simulator
276 
277  // signal we have received the elevation
278  // used by log display
279  emit this->receivedRequestedElevation(plane, callsign);
280  Q_UNUSED(isWater)
281  }
282 
283  void ISimulator::resetAircraftStatistics()
284  {
285  m_statsUpdateAircraftRuns = 0;
286  m_statsUpdateAircraftTimeAvgMs = 0;
287  m_statsUpdateAircraftTimeTotalMs = 0;
288  m_statsMaxUpdateTimeMs = 0;
289  m_statsCurrentUpdateTimeMs = 0;
290  m_statsPhysicallyAddedAircraft = 0;
291  m_statsPhysicallyRemovedAircraft = 0;
292  m_statsUpdateAircraftLimited = 0;
293  m_statsLastUpdateAircraftRequestedMs = 0;
294  m_statsUpdateAircraftRequestedDeltaMs = 0;
295  ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics();
296  }
297 
298  bool ISimulator::isEmulatedDriver() const
299  {
300  const QString className = this->metaObject()->className();
301  return className.contains("emulated", Qt::CaseInsensitive);
302  }
303 
304  bool ISimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
305  {
306  if (this->isMyIdentifier(originator)) { return false; }
307  if (this->isShuttingDown()) { return false; }
308 
309  if (commandLine.isEmpty()) { return false; }
310  CSimpleCommandParser parser({ ".plugin", ".drv", ".driver" });
311  parser.parse(commandLine);
312  if (!parser.isKnownCommand()) { return false; }
313 
314  // .plugin unload
315  if (parser.matchesPart(1, "unload"))
316  {
317  this->unload();
318  return true;
319  }
320 
321  // .plugin log interpolator
322  const QString part1(parser.part(1).toLower().trimmed());
323  if (part1.startsWith("logint") && parser.hasPart(2))
324  {
325  const QString part2 = parser.part(2).toLower();
326  if (part2 == "off" || part2 == "false")
327  {
328  CLogMessage(this).info(u"Disabled interpolation logging");
329  this->clearInterpolationLogCallsigns();
330  return true;
331  }
332  if (part2 == "clear" || part2 == "clr")
333  {
334  m_interpolationLogger.clearLog();
335  CLogMessage(this).info(u"Cleared interpolation logging");
336  this->clearInterpolationLogCallsigns();
337  return true;
338  }
339  if (part2.startsWith("max"))
340  {
341  if (!parser.hasPart(3)) { return false; }
342  bool ok {};
343  const int max = parser.part(3).toInt(&ok);
344  if (!ok) { return false; }
345  m_interpolationLogger.setMaxSituations(max);
346  CLogMessage(this).info(u"Max.situations logged: %1") << max;
347  return true;
348  }
349  if (part2 == "write" || part2 == "save")
350  {
351  // stop logging of other log
352  this->clearInterpolationLogCallsigns();
353 
354  // write
355  const bool clearLog = true;
356  m_interpolationLogger.writeLogInBackground(clearLog);
357  CLogMessage(this).info(u"Started writing interpolation log");
358  return true;
359  }
360  if (part2 == "show")
361  {
362  const QDir dir(CInterpolationLogger::getLogDirectory());
363  if (CDirectoryUtils::isDirExisting(dir))
364  {
365  const QUrl dirUrl = QUrl::fromLocalFile(dir.absolutePath());
366  QDesktopServices::openUrl(dirUrl); // show dir in browser
367  }
368  else { CLogMessage(this).warning(u"No interpolation log directory"); }
369  return true;
370  }
371 
372  const CCallsign cs(part2.toUpper());
373  if (!cs.isValid()) { return false; }
374  if (this->getAircraftInRangeCallsigns().contains(cs))
375  {
376  CLogMessage(this).info(u"Will log interpolation for '%1'") << cs.asString();
377  this->setLogCallsign(true, cs);
378  return true;
379  }
380  else
381  {
382  CLogMessage(this).warning(u"Cannot log interpolation for '%1', no aircraft in range") << cs.asString();
383  return false;
384  }
385  } // logint
386 
387  if (part1.startsWith("spline") || part1.startsWith("linear"))
388  {
389  if (parser.hasPart(2))
390  {
391  const CCallsign cs(parser.part(2));
392  const bool changed = this->setInterpolationMode(part1, cs);
393  CLogMessage(this).info(changed ? QStringLiteral("Changed interpolation mode for '%1'") :
394  QStringLiteral("Unchanged interpolation mode for '%1'"))
395  << cs.asString();
396  return true;
397  }
398  else
399  {
400  CInterpolationAndRenderingSetupGlobal setup = this->getInterpolationSetupGlobal();
401  const bool changed = setup.setInterpolatorMode(part1);
402  if (changed) { this->setInterpolationSetupGlobal(setup); }
403  CLogMessage(this).info(changed ? QStringLiteral("Changed interpolation mode globally") :
404  QStringLiteral("Unchanged interpolation mode"));
405  return true;
406  }
407  } // spline/linear
408 
409  if (part1.startsWith("pos"))
410  {
411  CCallsign cs(parser.part(2).toUpper());
412  if (!cs.isValid())
413  {
414  const CCallsignSet csSet = this->getLogCallsigns();
415  if (csSet.size() != 1) { return false; }
416 
417  // if there is just one we take that one
418  cs = *csSet.begin();
419  }
420 
421  this->setLogCallsign(true, cs);
422  CLogMessage(this).info(u"Display position for '%1'") << cs.asString();
423  this->displayLoggedSituationInSimulator(cs, true);
424  return true;
425  }
426 
427  if (parser.hasPart(2) && (part1.startsWith("aircraft") || part1.startsWith("ac")))
428  {
429  const QString part2 = parser.part(2).toLower();
430  if (parser.hasPart(3) && (part2.startsWith("readd") || part2.startsWith("re-add")))
431  {
432  const QString cs = parser.part(3).toUpper();
433  if (cs == "all")
434  {
435  this->physicallyRemoveAllRemoteAircraft();
436  const CStatusMessageList msgs = this->debugVerifyStateAfterAllAircraftRemoved();
437  this->clearAllRemoteAircraftData(); // "dot command"
438  if (!msgs.isEmpty()) { emit this->driverMessages(msgs); }
439  const CSimulatedAircraftList aircraft = this->getAircraftInRange();
440  for (const CSimulatedAircraft &a : aircraft)
441  {
442  if (a.isEnabled()) { this->logicallyAddRemoteAircraft(a); }
443  }
444  }
445  else if (CCallsign::isValidAircraftCallsign(cs))
446  {
447  this->logicallyReAddRemoteAircraft(cs);
448  return true;
449  }
450  return false;
451  }
452  if (parser.hasPart(3) && (part2.startsWith("rm") || part2.startsWith("remove")))
453  {
454  const QString cs = parser.part(3).toUpper();
455  if (CCallsign::isValidAircraftCallsign(cs)) { this->logicallyRemoveRemoteAircraft(cs); }
456  }
457 
458  return false;
459  }
460 
461  if (part1.startsWith("limit"))
462  {
463  const int perSecond = parser.toInt(2, -1);
464  this->limitToUpdatesPerSecond(perSecond);
465  CLogMessage(this).info(u"Remote aircraft updates limitations: %1") << this->updateAircraftLimitationInfo();
466  return true;
467  }
468 
469  // CG override
470  if (part1 == QStringView(u"cg"))
471  {
472  if (parser.part(2).startsWith("clear", Qt::CaseInsensitive))
473  {
474  CLogMessage(this).info(u"Clear all overridden CGs");
475  const CLengthPerCallsign cgsPerCallsign = this->clearCGOverrides();
476 
477  // restore all CGs
478  for (const CCallsign &cs : this->getAircraftInRangeCallsigns())
479  {
480  // reset CGs per callsign
481  const CLength cg = cgsPerCallsign.contains(cs) ? cgsPerCallsign[cs] : CLength::null();
482  this->updateCG(cs, cg);
483  }
484  return true;
485  }
486 
487  if (parser.hasPart(3))
488  {
489  // ms can be a string like "B773 B773_RR SDM"
490  const QString ms = parser.partAndRemainingStringAfter(3).toUpper();
491  CLength cg;
492  cg.parseFromString(parser.part(2), CPqString::SeparatorBestGuess);
493  if (!ms.isEmpty())
494  {
495  CLogMessage(this).info(u"Setting CG for '%1': %2") << ms << cg.valueRoundedWithUnit();
496  const bool set = this->insertCGForModelStringOverridden(cg, ms);
497  if (set)
498  {
499  const CCallsignSet callsigns = this->updateCGForModel(ms, cg);
500  if (!callsigns.isEmpty())
501  {
502  this->insertCGOverridden(cg, callsigns);
503  CLogMessage(this).info(u"Setting CG for '%1': %2")
504  << callsigns.getCallsignsAsString(true) << cg.valueRoundedWithUnit();
505  }
506  return true;
507 
508  } // set
509  } // model string
510 
511  } // 3 parts
512  }
513 
514  // driver specific cmd line arguments
515  return this->parseDetails(parser);
516  }
517 
518  void ISimulator::registerHelp()
519  {
520  if (CSimpleCommandParser::registered("swift::core::ISimulator")) { return; }
521  CSimpleCommandParser::registerCommand({ ".drv", "alias: .driver .plugin" });
522  CSimpleCommandParser::registerCommand({ ".drv unload", "unload driver" });
523  CSimpleCommandParser::registerCommand({ ".drv cg length clear|modelstr.", "override CG" });
524  CSimpleCommandParser::registerCommand(
525  { ".drv limit number/secs.", "limit updates to number per second (0..off)" });
526  CSimpleCommandParser::registerCommand({ ".drv logint callsign", "log interpolator for callsign" });
527  CSimpleCommandParser::registerCommand({ ".drv logint off", "no log information for interpolator" });
528  CSimpleCommandParser::registerCommand({ ".drv logint write", "write interpolator log to file" });
529  CSimpleCommandParser::registerCommand({ ".drv logint clear", "clear current log" });
530  CSimpleCommandParser::registerCommand({ ".drv logint max number", "max. number of entries logged" });
531  CSimpleCommandParser::registerCommand({ ".drv pos callsign", "show position for callsign" });
532  CSimpleCommandParser::registerCommand(
533  { ".drv spline|linear callsign", "set spline/linear interpolator for one/all callsign(s)" });
534  CSimpleCommandParser::registerCommand(
535  { ".drv aircraft readd callsign", "add again (re-add) a given callsign" });
536  CSimpleCommandParser::registerCommand({ ".drv aircraft readd all", "add again (re-add) all aircraft" });
537  CSimpleCommandParser::registerCommand(
538  { ".drv aircraft rm callsign", "remove a given callsign from simulator" });
539  }
540 
541  QString ISimulator::statusToString(SimulatorStatus status)
542  {
543  QStringList s;
544  if (status.testFlag(Unspecified)) { s << QStringLiteral("Unspecified"); }
545  if (status.testFlag(Disconnected)) { s << QStringLiteral("Disconnected"); }
546  if (status.testFlag(Connected)) { s << QStringLiteral("Connected"); }
547  if (status.testFlag(Simulating)) { s << QStringLiteral("Simulating"); }
548  if (status.testFlag(Paused)) { s << QStringLiteral("Paused"); }
549  return s.join(", ");
550  }
551 
552  bool ISimulator::isEqualLastSent(const CAircraftSituation &compare) const
553  {
554  Q_ASSERT_X(compare.hasCallsign(), Q_FUNC_INFO, "Need callsign");
555  if (!m_lastSentSituations.contains(compare.getCallsign())) { return false; }
556  if (compare.isNull()) { return false; }
557  return compare.equalPbhVectorAltitudeElevation(m_lastSentSituations.value(compare.getCallsign()));
558  // return compare.equalPbhVectorAltitude(m_lastSentSituations.value(compare.getCallsign()));
559  }
560 
561  bool ISimulator::isEqualLastSent(const CAircraftParts &compare, const CCallsign &callsign) const
562  {
563  if (callsign.isEmpty()) { return false; }
564  if (!m_lastSentParts.contains(callsign)) { return false; }
565  return compare.equalValues(m_lastSentParts.value(callsign));
566  }
567 
568  void ISimulator::rememberLastSent(const CAircraftSituation &sent)
569  {
570  // normally we should never end up without callsign, but it has happened in real world scenarios
571  // https://discordapp.com/channels/539048679160676382/568904623151382546/575712119513677826
572  const bool hasCs = sent.hasCallsign();
573  SWIFT_VERIFY_X(hasCs, Q_FUNC_INFO, "Need callsign");
574  if (!hasCs) { return; }
575  m_lastSentSituations.insert(sent.getCallsign(), sent);
576  }
577 
578  void ISimulator::rememberLastSent(const CAircraftParts &sent, const CCallsign &callsign)
579  {
580  // normally we should never end up without callsign, but it has happened in real world scenarios
581  // https://discordapp.com/channels/539048679160676382/568904623151382546/575712119513677826
582  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
583  if (callsign.isEmpty()) { return; }
584  m_lastSentParts.insert(callsign, sent);
585  }
586 
587  CAircraftSituationList ISimulator::getLastSentCanLikelySkipNearGroundInterpolation() const
588  {
589  const QList<CAircraftSituation> situations = m_lastSentSituations.values();
590  CAircraftSituationList skipped;
591  for (const CAircraftSituation &s : situations)
592  {
593  if (s.canLikelySkipNearGroundInterpolation()) { skipped.push_back(s); }
594  }
595  return skipped;
596  }
597 
598  bool ISimulator::isAnyConnectedStatus(SimulatorStatus status)
599  {
600  return (status.testFlag(Connected) || status.testFlag(Simulating) || status.testFlag(Paused));
601  }
602 
603  const CCallsign &ISimulator::getTestCallsign()
604  {
605  static const CCallsign cs("SWIFT");
606  return cs;
607  }
608 
609  ISimulator::ISimulator(const CSimulatorPluginInfo &pluginInfo, IOwnAircraftProvider *ownAircraftProvider,
610  IRemoteAircraftProvider *remoteAircraftProvider, IClientProvider *clientProvider,
611  QObject *parent)
612  : QObject(parent), COwnAircraftAware(ownAircraftProvider), CRemoteAircraftAware(remoteAircraftProvider),
614  CIdentifiable(this)
615  {
616  this->setObjectName("Simulator: " + pluginInfo.getIdentifier());
617  m_interpolationLogger.setObjectName("Logger: " + pluginInfo.getIdentifier());
618 
620 
621  // provider signals, hook up with remote aircraft provider
622  m_remoteAircraftProviderConnections.append(
623  CRemoteAircraftAware::provider()->connectRemoteAircraftProviderSignals(
624  this, // receiver must match object in bind
625  nullptr, nullptr,
626  [](const aviation::CCallsign &) {
627  /* currently not used, the calls are handled by context call logicallyRemoveRemoteAircraft*/
628  },
629  [this](const CAirspaceAircraftSnapshot &snapshot) {
630  this->rapOnRecalculatedRenderedAircraft(snapshot);
631  }));
632 
633  // swift data
634  if (sApp && sApp->hasWebDataServices())
635  {
640  }
642 
643  // provider
644  if (pluginInfo.isEmulatedPlugin() && !pluginInfo.getSimulatorInfo().isSingleSimulator())
645  {
646  // emulated driver with NO info yet
647  CLogMessage(this).info(u"Plugin '%1' with no simulator info yet, hope it will be set later")
648  << pluginInfo.getIdentifier();
649  }
650  else
651  {
652  // NORMAL CASE or plugin with info already set
653  this->setNewPluginInfo(pluginInfo, m_multiSettings.getSettings(pluginInfo.getSimulatorInfo()));
654  }
655 
656  // info data
659 
660  // model changed
662 
663  // info
664  CLogMessage(this).info(u"Initialized simulator driver: '%1'")
666  this->getSimulatorInfo().toQString());
667  }
668 
670  {
671  if (!snapshot.isValidSnapshot()) { return; }
672 
673  // for unrestricted values all add/remove actions are directly linked
674  // when changing back from restricted->unrestricted an one time update is required
675  if (!snapshot.isRestricted() && !snapshot.isRestrictionChanged()) { return; }
676 
677  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Needs to run in object thread");
678  Q_ASSERT_X(snapshot.generatingThreadName() != QThread::currentThread()->objectName(), Q_FUNC_INFO,
679  "Expect snapshot from background thread");
680 
681  // restricted snapshot values?
682  bool changed = false;
683  if (snapshot.isRenderingEnabled())
684  {
685  // make sure not to add aircraft again which are no longer in range
686  const CCallsignSet callsignsInRange = this->getAircraftInRangeCallsigns();
687  const CCallsignSet callsignsEnabledAndStillInRange =
688  snapshot.getEnabledAircraftCallsignsByDistance().intersection(callsignsInRange);
689  const CCallsignSet callsignsInSimulator(this->physicallyRenderedAircraft()); // state in simulator
690  const CCallsignSet callsignsToBeRemoved(callsignsInSimulator.difference(callsignsEnabledAndStillInRange));
691  const CCallsignSet callsignsToBeAdded(callsignsEnabledAndStillInRange.difference(callsignsInSimulator));
692  if (!callsignsToBeRemoved.isEmpty())
693  {
694  const int r = this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved);
695  changed = r > 0;
696  }
697 
698  if (!callsignsToBeAdded.isEmpty())
699  {
700  CSimulatedAircraftList aircraftToBeAdded(
701  this->getAircraftInRange().findByCallsigns(callsignsToBeAdded)); // thread safe copy
702  for (const CSimulatedAircraft &aircraft : aircraftToBeAdded)
703  {
704  Q_ASSERT_X(aircraft.isEnabled(), Q_FUNC_INFO, "Disabled aircraft detected as to be added");
705  Q_ASSERT_X(aircraft.hasModelString(), Q_FUNC_INFO, "Missing model string");
706  this->callPhysicallyAddRemoteAircraft(aircraft); // recalculate snapshot
707  changed = true;
708  }
709  }
710  }
711  else
712  {
713  // no rendering at all, we remove everything
714  const int r = this->physicallyRemoveAllRemoteAircraft();
715  changed = r > 0;
716  }
717 
718  // we have handled snapshot
719  if (changed) { emit this->airspaceSnapshotHandled(); }
720  }
721 
723  {
724  this->resetLastSentValues(callsign);
725  return true;
726  }
727 
729  {
730  if (callsigns.isEmpty()) { return 0; }
731  int removed = 0;
732  for (const CCallsign &callsign : callsigns)
733  {
734  this->callPhysicallyRemoveRemoteAircraft(callsign);
735  removed++;
736  }
737  return removed;
738  }
739 
741  {
742  // a default implementation, but normally overridden by the sims
743  const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
744 
745  // normally that would be already done in the specialized implementation
746  const int r = this->physicallyRemoveMultipleRemoteAircraft(callsigns);
747 
748  // leave no trash
749  this->clearAllRemoteAircraftData(); // remove all aircraft
750  return r;
751  }
752 
754  {
755  // void, can be overridden in specialized drivers
756  }
757 
759  {
760  // void, can be overridden in specialized drivers
761  }
762 
764  {
769  }
770 
772  bool likelyOnGroundElevation, const CElevationPlane &elevation,
773  const CLength &simulatorCG)
774  {
775  if (callsign.isEmpty()) { return; }
776  if (elevation.hasMSLGeodeticHeight())
777  {
778  const int aircraftCount = this->getAircraftInRangeCount();
780  aircraftCount *
781  3); // at least 3 elevations per aircraft, even better as not all are requesting elevations
782  this->rememberGroundElevation(callsign, likelyOnGroundElevation, elevation);
783  }
784 
785  const QString modelString = model.getModelString();
786  if (modelString.isEmpty()) { return; }
787 
788  // this value now is the simulator or overridden value
789  const CLength cgOvr = this->overriddenCGorDefault(simulatorCG, modelString);
790  if (!cgOvr.isNull() && !this->hasSameSimulatorCG(cgOvr, callsign))
791  {
792  // the value did change
793  const CSimulatorSettings::CGSource source =
795  if (source != CSimulatorSettings::CGFromDBOnly)
796  {
797  this->insertCG(cgOvr, modelString, callsign); // per model string and CG
798  }
799 
800  // here we know we have a valid model and CG did change
801  const CSimulatorInfo sim = this->getSimulatorInfo();
802  m_autoPublishing.insert(modelString,
803  simulatorCG); // still using simulator CG here, not the overridden value
804 
805  // if simulator did change, add as well
806  if (!model.getSimulator().matchesAll(sim))
807  {
808  m_autoPublishing.insert(modelString, this->getSimulatorInfo());
809  }
810  }
811  }
812 
813  void ISimulator::emitSimulatorCombinedStatus(SimulatorStatus oldStatus)
814  {
815  const SimulatorStatus newStatus = this->getSimulatorStatus();
816  if (oldStatus != newStatus)
817  {
818  // decouple, follow up of signal can include unloading
819  // simulator so this should happen strictly asynchronously (which is like forcing Qt::QueuedConnection)
820  QPointer<ISimulator> myself(this);
821  QTimer::singleShot(0, this, [=, this] {
822  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
823 
824  // now simulating
825  if (newStatus.testFlag(Simulating))
826  {
827  this->setUpdateAllRemoteAircraft(); // force an update of every remote aircraft
828  }
829 
830  emit this->simulatorStatusChanged(
831  newStatus); // only place where we should emit the signal, use emitSimulatorCombinedStatus to emit
832  });
833  }
834  }
835 
837  {
838  QPointer<ISimulator> myself(this);
839  QTimer::singleShot(5, this, [=, this] {
840  if (!myself) { return; }
842  });
843  }
844 
846  {
847  if (!IInterpolationSetupProvider::setInterpolationSetupGlobal(setup)) { return false; }
848  const bool r = setup.isRenderingRestricted();
849  const bool e = setup.isRenderingEnabled();
850 
851  if (!this->isShuttingDown())
852  {
853  CCrashHandler::instance()->crashAndLogAppendInfo(u"Rendering setup: " % setup.toQString(true));
854  }
856  return true;
857  }
858 
860  {
861  return m_loopbackSituations.value(callsign);
862  }
863 
865  {
866  bool modified = false;
867  const CAircraftModel reverseModel =
868  CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified);
869  return reverseModel;
870  }
871 
872  bool ISimulator::isUpdateAircraftLimited(qint64 timestamp)
873  {
874  if (!m_limitUpdateAircraft) { return false; }
875  const bool hasToken = m_limitUpdateAircraftBucket.tryConsume(1, timestamp);
876  return !hasToken;
877  }
878 
880  {
881  const bool limited = this->isUpdateAircraftLimited(startTime);
882  return limited;
883  }
884 
885  bool ISimulator::limitToUpdatesPerSecond(int numberPerSecond)
886  {
887  if (numberPerSecond < 1)
888  {
889  m_limitUpdateAircraft = false;
890  return false;
891  }
892 
893  int tokens = qRound(0.1 * numberPerSecond); // 100ms
894  do {
895  if (tokens >= 3)
896  {
898  break;
899  }
900  tokens = qRound(0.25 * numberPerSecond); // 250ms
901  if (tokens >= 3)
902  {
904  break;
905  }
906  tokens = qRound(0.5 * numberPerSecond); // 500ms
907  if (tokens >= 3)
908  {
910  break;
911  }
912  tokens = numberPerSecond;
914  }
915  while (false);
916 
918  m_limitUpdateAircraft = true;
919  return true;
920  }
921 
923  {
924  if (!m_limitUpdateAircraft) { return QStringLiteral("not limited"); }
925  static const QString limInfo("Limited %1 time(s) with %2/secs.");
927  }
928 
930  {
933  }
934 
936  {
937  m_lastSentParts.remove(callsign);
938  m_lastSentSituations.remove(callsign);
939  }
940 
942  {
943  this->disconnectFrom(); // disconnect from simulator
944  const bool saved = m_autoPublishing.writeJsonToFile(); // empty data are ignored
945  if (saved) { emit this->autoPublishDataWritten(this->getSimulatorInfo()); }
947  m_remoteAircraftProviderConnections.disconnectAll(); // disconnect signals from provider
948  }
949 
951  {
952  return this->isTestMode() || this->isAircraftInRange(callsign);
953  }
954 
956  {
957  m_averageFps = -1.0;
958  m_simTimeRatio = 1.0;
959  m_trackMilesShort = 0.0;
960  m_minutesLate = 0.0;
961  return true;
962  }
963 
965  {
966  if (this->isShuttingDown()) { return false; }
967  if (callsign.isEmpty()) { return false; }
968 
969  this->logicallyRemoveRemoteAircraft(callsign);
970  if (!this->isAircraftInRange(callsign)) { return false; }
971  const QPointer<ISimulator> myself(this);
972  QTimer::singleShot(2500, this, [=, this] {
973  if (myself.isNull()) { return; }
974  if (this->isShuttingDown()) { return; }
975  if (!this->isAircraftInRange(callsign)) { return; }
976  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
977  if (aircraft.isEnabled() && aircraft.hasModelString()) { this->logicallyAddRemoteAircraft(aircraft); }
978  });
979  return true;
980  }
981 
983  {
984  if (this->isShuttingDown()) { return false; }
985  return aircraft.isEnabled() ? this->physicallyAddRemoteAircraft(aircraft) :
987  }
988 
990  {
991  // we expect the new model "in aircraft"
992  // remove upfront, and then enable / disable again
993  if (this->isShuttingDown()) { return false; }
994  const CCallsign callsign = aircraft.getCallsign();
995  if (!this->isPhysicallyRenderedAircraft(callsign)) { return false; }
996  this->physicallyRemoveRemoteAircraft(callsign);
997  // return this->changeRemoteAircraftEnabled(aircraft);
998 
999  const QPointer<ISimulator> myself(this);
1000  QTimer::singleShot(1000, this, [=, this] {
1001  if (!myself) { return; }
1002  if (this->isAircraftInRange(callsign)) { this->changeRemoteAircraftEnabled(aircraft); }
1003  });
1004  return true;
1005  }
1006 
1008  {
1009  CStatusMessageList msgs;
1010  if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; }
1012  {
1013  msgs.push_back(CStatusMessage(this).error(u"m_addAgainAircraftWhenRemoved not empty: '%1'")
1015  }
1016  return msgs;
1017  }
1018 
1020  const QString &details) const
1021  {
1022  static const QString msg("Interpolation ('%1'): '%2'");
1023  const QString m = msg.arg(callsign.asString(), status.toQString());
1024  if (details.isEmpty()) { return m; }
1025 
1026  static const QString addDetails(" details: '%1'");
1027  return m % addDetails.arg(details);
1028  }
1029 
1030  void ISimulator::finishUpdateRemoteAircraftAndSetStatistics(qint64 startTime, bool limited)
1031  {
1032  const qint64 now = QDateTime::currentMSecsSinceEpoch();
1033  const qint64 dt = now - startTime;
1038  static_cast<double>(m_statsUpdateAircraftTimeTotalMs) / static_cast<double>(m_statsUpdateAircraftRuns);
1041 
1042  if (!this->isUpdateAllRemoteAircraft(startTime)) { this->resetUpdateAllRemoteAircraft(); }
1043 
1046  {
1048  }
1049  if (limited) { m_statsUpdateAircraftLimited++; }
1050  }
1051 
1053  {
1054  Q_UNUSED(newModel)
1055  // can be overridden
1056  }
1057 
1059  {
1060  const bool updated = this->updateOwnSituation(situation);
1061 
1062  // do not use every situation, but every deltaMs and only on ground
1063  constexpr qint64 deltaMs = 5000;
1064  if (situation.isOnGround() && situation.getTimeDifferenceAbsMs(m_lastRecordedGndElevationMs) > deltaMs)
1065  {
1068  if (settings.isRecordOwnAircraftGnd())
1069  {
1070  const CSimulatedAircraft ownAircraft = this->getOwnAircraft();
1071  CAltitude elevation = situation.getGroundElevation();
1072  if (elevation.isNull())
1073  {
1074  // calculate elevation
1075  const CLength cg = ownAircraft.getModel().getCG();
1076  elevation = (cg.isNull() || situation.getAltitude().isNull()) ?
1077  CAltitude::null() :
1078  (situation.getAltitude().withOffset(cg * -1.0));
1079  }
1080 
1081  // own ground elevations
1082  if (elevation.hasMeanSeaLevelValue())
1083  {
1084  const CCallsign cs = situation.hasCallsign() ? situation.getCallsign() : ownAircraft.getCallsign();
1085  const CLength radius = settings.getRecordedGndRadius().isNull() ?
1086  CElevationPlane::singlePointRadius() :
1087  settings.getRecordedGndRadius();
1088  const CElevationPlane ep(situation, radius);
1089  const bool remembered = this->rememberGroundElevation(cs, situation.isOnGround(), ep, radius);
1090 
1091  if (CBuildConfig::isLocalDeveloperDebugBuild())
1092  {
1093  const bool invalid = situation.isOnGround() && elevation.isZeroEpsilonConsidered();
1094  SWIFT_AUDIT_X(!invalid, Q_FUNC_INFO, "On ground in water");
1095  }
1096  Q_UNUSED(remembered) // false means it was already in that cache, or something else is wrong
1097  }
1098  }
1099  }
1100  return updated;
1101  }
1102 
1104  {
1105  const CSimulatorInfo simulator = this->getSimulatorInfo();
1106  if (!simulator.isSingleSimulator()) { return {}; }
1107 
1108  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
1109  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
1110  }
1111 
1113  {
1114  const CAircraftModel model = aircraft.getModel();
1115  if (!aircraft.hasCallsign())
1116  {
1117  CLogMessage(this).warning(u"Missing callsign for '%1'") << aircraft.getModelString();
1118  return false;
1119  }
1120  if (!model.hasModelString())
1121  {
1122  CLogMessage(this).warning(u"No model string for callsign '%1'") << aircraft.getCallsign();
1123  return false;
1124  }
1125  if (model.isCallsignEmpty())
1126  {
1127  CLogMessage(this).warning(u"No callsign for model of aircraft '%1'") << aircraft.getCallsign();
1128  return false;
1129  }
1130  return true;
1131  }
1132 
1134  {
1135  CLogMessage(this).info(u"Adding '%1' '%2' to '%3'")
1136  << aircraft.getCallsign() << aircraft.getModel().getModelStringAndDbKey()
1137  << this->getSimulatorInfo().toQString(true);
1138  }
1139 
1141  {
1144 
1145  static const QString sep("\n------\n");
1146  QString dm;
1147  if (s.tsCurrent > 0)
1148  {
1149  dm = u"Setup: " % s.usedSetup.toQString(true) % u"\n\n" % u"Situation: " %
1150  s.toQString(false, true, true, true, true, sep);
1151  }
1152  if (p.tsCurrent > 0) { dm += (dm.isEmpty() ? u"Parts: " : u"\n\nParts: ") % p.toQString(sep); }
1153  return dm;
1154  }
1155 
1156  void ISimulator::rapOnRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot)
1157  {
1158  if (!this->isConnected()) { return; }
1159  if (this->isShuttingDown()) { return; }
1160  this->onRecalculatedRenderedAircraft(snapshot);
1161  }
1162 
1163  void ISimulator::callPhysicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft)
1164  {
1165  m_statsPhysicallyAddedAircraft++;
1166  this->physicallyAddRemoteAircraft(remoteAircraft);
1167  }
1168 
1169  void ISimulator::callPhysicallyRemoveRemoteAircraft(const CCallsign &remoteCallsign)
1170  {
1171  this->clearData(remoteCallsign);
1172  this->physicallyRemoveRemoteAircraft(remoteCallsign);
1173  }
1174 
1175  void ISimulator::displayLoggedSituationInSimulator(const CCallsign &cs, bool stopLogging, int times)
1176  {
1177  if (cs.isEmpty()) { return; }
1178  if (this->isShuttingDown()) { return; }
1180  const bool logsCs = setup.logInterpolation();
1181  if (!logsCs) { return; }
1182 
1183  stopLogging = stopLogging || !this->isSimulating(); // stop when sim was stopped
1184  stopLogging = stopLogging && logsCs;
1185  if (!stopLogging && times < 1) { return; }
1186 
1187  const bool inRange = this->getAircraftInRangeCallsigns().contains(cs);
1188  if (!stopLogging && !inRange) { return; }
1189  if (stopLogging && (times < 1 || !inRange))
1190  {
1191  this->setLogCallsign(false, cs);
1192  return;
1193  }
1194 
1195  const QString dm = this->latestLoggedDataFormatted(cs);
1196  if (!dm.isEmpty()) { this->displayStatusMessage(CStatusMessage(this).info(dm)); }
1197 
1198  const int t = CMathUtils::randomInteger(4500, 5500); // makes sure not always using the same time difference
1199  const QPointer<ISimulator> myself(this);
1200  QTimer::singleShot(t, this, [=, this] {
1201  if (!myself || myself->isShuttingDown()) { return; }
1202  this->displayLoggedSituationInSimulator(cs, stopLogging, times - 1);
1203  });
1204  }
1205 
1207  {
1208  CAircraftModel model = this->getOwnAircraftModel();
1209  model.setModelString(modelString);
1211  }
1212 
1214  {
1215  if (!model.hasModelString()) { return; }
1216  if (this->isShuttingDown()) { return; }
1217  Q_ASSERT_X(sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web services");
1218 
1219  if (this->getOwnAircraftModel() != model)
1220  {
1221  if (CDatabaseUtils::hasDbAircraftData())
1222  {
1224  const bool updated = this->updateOwnModel(newModel); // update in provider (normally the context)
1225  if (updated) { emit this->ownAircraftModelChanged(this->getOwnAircraftModel()); }
1226  }
1227  else
1228  {
1229  // we wait for the data
1231  [=, this] { this->reverseLookupAndUpdateOwnAircraftModel(model); });
1232  }
1233  }
1234  }
1235 
1237  {
1238  this->setObjectName("ISimulatorListener:" + info.toQString());
1239 
1240  // stop listener after it reports simulator ready
1241  bool s =
1243  Q_ASSERT_X(s, Q_FUNC_INFO, "connect failed");
1244 
1245  if (sApp)
1246  {
1247  s = connect(sApp, &CApplication::aboutToShutdown, this, &ISimulatorListener::onAboutToShutdown,
1249  Q_ASSERT_X(s, Q_FUNC_INFO, "connect failed");
1250  }
1251 
1252  Q_UNUSED(s)
1253  }
1254 
1256 
1257  bool ISimulatorListener::isShuttingDown() const { return (!sApp || sApp->isShuttingDown() || m_aboutToShutdown); }
1258 
1259  void ISimulatorListener::onAboutToShutdown()
1260  {
1261  if (!m_aboutToShutdown) { return; }
1262  m_aboutToShutdown = true;
1263  this->stop();
1264  }
1265 
1267  {
1268  if (m_isRunning) { return; }
1269  if (!CThreadUtils::isInThisThread(this))
1270  {
1271  // call in correct thread
1272  QPointer<ISimulatorListener> myself(this);
1273  QTimer::singleShot(0, this, [=, this] {
1274  if (myself) { this->start(); }
1275  });
1276  return;
1277  }
1278 
1279  m_isRunning = true;
1280  this->startImpl();
1281  }
1282 
1284  {
1285  if (!m_isRunning) { return; }
1286  if (!CThreadUtils::isInThisThread(this))
1287  {
1288  // call in correct thread
1289  QPointer<ISimulatorListener> myself(this);
1290  QTimer::singleShot(0, this, [=, this] {
1291  if (myself) { this->stop(); }
1292  });
1293  return;
1294  }
1295 
1296  this->stopImpl();
1297  m_isRunning = false;
1298  }
1299 
1301  {
1302  if (!m_isRunning) { return; }
1303  if (!CThreadUtils::isInThisThread(this))
1304  {
1305  // call in correct thread
1306  QPointer<ISimulatorListener> myself(this);
1307  QTimer::singleShot(0, this, [=, this] {
1308  if (myself) { this->check(); }
1309  });
1310  return;
1311  }
1312 
1313  this->checkImpl();
1314  }
1315 } // namespace swift::core
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
void aboutToShutdown()
About to shutdown.
bool hasWebDataServices() const
Web data services available?
bool isShuttingDown() const
Is application shutting down?
CWebDataServices * getWebDataServices() const
Get the web data services.
void swiftDbModelMatchingEntitiesRead()
All entities needed for model matching.
void swiftDbAllDataRead()
All swift DB data have been read.
virtual bool isPhysicallyRenderedAircraft(const swift::misc::aviation::CCallsign &callsign) const =0
Is the aircraft rendered (displayed in simulator)? This shall only return true if the aircraft is rea...
virtual bool logicallyReAddRemoteAircraft(const swift::misc::aviation::CCallsign &callsign)
Removes and adds again the aircraft.
Definition: simulator.cpp:964
swift::misc::simulation::settings::CMultiSimulatorSettings m_multiSettings
simulator settings for all simulators
Definition: simulator.h:600
void resetLastSentValues()
Reset the last sent values.
Definition: simulator.cpp:929
double m_simTimeRatio
ratio of simulation time to real time, due to low FPS (X-Plane)
Definition: simulator.h:566
void resetUpdateAllRemoteAircraft()
Reset.
Definition: simulator.cpp:233
void renderRestrictionsChanged(bool restricted, bool enabled, int maxAircraft, const swift::misc::physical_quantities::CLength &maxRenderedDistance)
Render restrictions have been changed.
bool setInterpolationSetupGlobal(const swift::misc::simulation::CInterpolationAndRenderingSetupGlobal &setup)
Set the global setup.
Definition: simulator.cpp:845
bool updateOwnSituationAndGroundElevation(const swift::misc::aviation::CAircraftSituation &situation)
Update own aircraft position and if suitable use it to update ground elevation.
Definition: simulator.cpp:1058
virtual void onOwnModelChanged(const swift::misc::simulation::CAircraftModel &newModel)
Own model has been changed.
Definition: simulator.cpp:1052
bool isUpdateAllRemoteAircraft(qint64 currentTimestamp=-1) const
Do update all remote aircraft?
Definition: simulator.cpp:218
double m_averageFps
FPS.
Definition: simulator.h:565
virtual bool isShuttingDown() const
Is overall (swift) application shutting down.
Definition: simulator.h:214
QString updateAircraftLimitationInfo() const
Info about update aircraft limitations.
Definition: simulator.cpp:922
virtual bool logicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign)
Logically remove remote aircraft from simulator. Depending on max. aircraft, enabled status etc....
Definition: simulator.cpp:64
bool limitToUpdatesPerSecond(int numberPerSecond)
Limit to updates per second.
Definition: simulator.cpp:885
virtual void onSwiftDbAllDataRead()
When swift DB data are read.
Definition: simulator.cpp:753
bool m_updateRemoteAircraftInProgress
currently updating remote aircraft
Definition: simulator.h:559
swift::misc::CTokenBucket m_limitUpdateAircraftBucket
means 50 per second
Definition: simulator.h:596
virtual swift::misc::aviation::CCallsignSet physicallyRenderedAircraft() const =0
Physically rendered (displayed in simulator) This shall only return aircraft handled in the simulator...
qint64 m_lastRecordedGndElevationMs
when gnd.elevation was last modified
Definition: simulator.h:574
virtual void clearAllRemoteAircraftData()
Clear all aircraft related data, but do not physically remove the aircraft.
Definition: simulator.cpp:128
void rememberElevationAndSimulatorCG(const swift::misc::aviation::CCallsign &callsign, const swift::misc::simulation::CAircraftModel &model, bool likelyOnGroundElevation, const swift::misc::geo::CElevationPlane &elevation, const swift::misc::physical_quantities::CLength &simulatorCG)
Set elevation and CG in the providers and for auto publishing.
Definition: simulator.cpp:771
double m_trackMilesShort
difference between real and reported groundspeed, multiplied by time
Definition: simulator.h:567
virtual bool physicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign)=0
Remove remote aircraft from simulator.
Definition: simulator.cpp:722
void ownAircraftModelChanged(const swift::misc::simulation::CAircraftModel &model)
Emitted when own aircraft model has changed.
double m_minutesLate
difference between real and reported groundspeed, integrated over time
Definition: simulator.h:568
bool isAircraftInRangeOrTestMode(const swift::misc::aviation::CCallsign &callsign) const
Test version aware version of isAircraftInRange.
Definition: simulator.cpp:950
void finishUpdateRemoteAircraftAndSetStatistics(qint64 startTime, bool limited=false)
Update stats and flags.
Definition: simulator.cpp:1030
swift::misc::aviation::CAircraftSituationList getLoopbackSituations(const swift::misc::aviation::CCallsign &callsign) const
The traced loopback situations.
Definition: simulator.cpp:859
virtual bool physicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft)=0
Add new remote aircraft physically to the simulator.
void reverseLookupAndUpdateOwnAircraftModel(const swift::misc::simulation::CAircraftModel &model)
Set own model.
Definition: simulator.cpp:1213
int m_statsUpdateAircraftLimited
skipped because of max.update limitations
Definition: simulator.h:563
swift::misc::aviation::CAircraftPartsPerCallsign m_lastSentParts
last parts sent to simulator
Definition: simulator.h:586
virtual void clearData(const swift::misc::aviation::CCallsign &callsign)
Clear the related data as statistics etc.
Definition: simulator.cpp:177
void emitInterpolationSetupChanged()
Pseudo signal, override to emit signal.
Definition: simulator.cpp:836
swift::misc::simulation::CAutoPublishData m_autoPublishing
for the DB
Definition: simulator.h:583
virtual void displayStatusMessage(const swift::misc::CStatusMessage &message) const =0
Display a status message in the simulator.
qint64 m_statsUpdateAircraftRequestedDeltaMs
delta time between 2 aircraft updates
Definition: simulator.h:576
bool isTestMode() const
Test mode? (driver can skip code parts etc., driver dependent)
Definition: simulator.h:183
virtual void onRecalculatedRenderedAircraft(const swift::misc::simulation::CAirspaceAircraftSnapshot &snapshot)
Recalculate the rendered aircraft, this happens when restrictions are applied (max....
Definition: simulator.cpp:669
virtual void unload()
Driver will be unloaded.
Definition: simulator.cpp:941
swift::misc::simulation::CAircraftModelList getModelSet() const
Get the model set.
Definition: simulator.cpp:1103
bool isUpdateAircraftLimited(qint64 timestamp=-1)
Limit reached (max number of updates by token bucket if enabled)
Definition: simulator.cpp:872
virtual swift::misc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const
Debug function to check state after all aircraft have been removed.
Definition: simulator.cpp:1007
virtual bool disconnectFrom()
Disconnect from simulator.
Definition: simulator.cpp:955
bool validateModelOfAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft) const
Validate if model has callsign and such.
Definition: simulator.cpp:1112
void emitSimulatorCombinedStatus(SimulatorStatus oldStatus=Unspecified)
Emit the combined status.
Definition: simulator.cpp:813
int m_statsUpdateAircraftRuns
statistics update count
Definition: simulator.h:562
swift::misc::simulation::CSimulatedAircraftList m_addAgainAircraftWhenRemoved
add this model again when removed, normally used to change model
Definition: simulator.h:590
bool m_limitUpdateAircraft
limit the update frequency by using swift::misc::CTokenBucket
Definition: simulator.h:597
swift::misc::simulation::CSimulatorInternals m_simulatorInternals
setup read from the sim
Definition: simulator.h:581
virtual void onSwiftDbModelMatchingEntitiesRead()
When swift DB data are read.
Definition: simulator.cpp:758
void autoPublishDataWritten(const swift::misc::simulation::CSimulatorInfo &simulator)
Auto publish data written for simulator.
void airspaceSnapshotHandled()
An airspace snapshot was handled.
virtual int physicallyRemoveAllRemoteAircraft()
Remove all remote aircraft and their data via ISimulator::clearAllRemoteAircraftData.
Definition: simulator.cpp:740
void logAddingAircraftModel(const swift::misc::simulation::CSimulatedAircraft &aircraft) const
Unified qeeing aircraft message.
Definition: simulator.cpp:1133
SimulatorStatusFlag
ISimulator status.
Definition: simulator.h:67
@ Simulating
Is the simulator actually simulating?
Definition: simulator.h:71
virtual SimulatorStatus getSimulatorStatus() const
Combined status.
Definition: simulator.cpp:54
qint64 m_statsUpdateAircraftTimeTotalMs
statistics total update time
Definition: simulator.h:571
swift::misc::simulation::settings::CSpecializedSimulatorSettings getSimulatorSettings() const
Settings for current simulator.
Definition: simulator.h:162
qint64 m_statsLastUpdateAircraftRequestedMs
when was the last aircraft update requested
Definition: simulator.h:575
swift::misc::simulation::CInterpolationLogger m_interpolationLogger
log.interpolation
Definition: simulator.h:582
bool isUpdateAircraftLimitedWithStats(qint64 startTime=-1)
Limited as ISimulator::isUpdateAircraftLimited plus updating statistics.
Definition: simulator.cpp:879
double m_statsUpdateAircraftTimeAvgMs
statistics average update time
Definition: simulator.h:564
virtual int physicallyRemoveMultipleRemoteAircraft(const swift::misc::aviation::CCallsignSet &callsigns)
Remove remote aircraft from simulator.
Definition: simulator.cpp:728
swift::misc::aviation::CAircraftSituationListPerCallsign m_loopbackSituations
traced loopback situations
Definition: simulator.h:593
virtual void initSimulatorInternals()
Init the internals info from the simulator.
Definition: simulator.cpp:763
qint64 m_statsMaxUpdateTimeMs
statistics max.update time
Definition: simulator.h:573
virtual bool isSimulating() const
Simulator running?
Definition: simulator.h:177
virtual bool isConnected() const =0
Are we connected to the simulator?
static swift::misc::simulation::CAircraftModel reverseLookupModel(const swift::misc::simulation::CAircraftModel &model)
Lookup against DB data.
Definition: simulator.cpp:864
QString latestLoggedDataFormatted(const swift::misc::aviation::CCallsign &cs) const
The latest logged data formatted.
Definition: simulator.cpp:1140
swift::misc::aviation::CAircraftSituationPerCallsign m_lastSentSituations
last situations sent to simulator
Definition: simulator.h:585
virtual bool logicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft)
Logically add a new aircraft. Depending on max. aircraft, enabled status etc. it will physically adde...
Definition: simulator.cpp:78
virtual bool changeRemoteAircraftModel(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Change remote aircraft per property.
Definition: simulator.cpp:989
static void registerHelp()
Register help.
Definition: simulator.cpp:518
qint64 m_statsCurrentUpdateTimeMs
statistics current update time
Definition: simulator.h:572
void interpolationAndRenderingSetupChanged()
Interpolation or rendering setup changed.
virtual bool changeRemoteAircraftEnabled(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Aircraft got enabled / disabled.
Definition: simulator.cpp:982
QString getInvalidSituationLogMessage(const swift::misc::aviation::CCallsign &callsign, const swift::misc::simulation::CInterpolationStatus &status, const QString &details={}) const
Info about invalid situation.
Definition: simulator.cpp:1019
virtual void startImpl()=0
Plugin specific implementation to start listener.
void simulatorStarted(const swift::misc::simulation::CSimulatorPluginInfo &info)
Emitted when the listener discovers the simulator running.
ISimulatorListener(const swift::misc::simulation::CSimulatorPluginInfo &info)
Constructor.
Definition: simulator.cpp:1236
virtual bool isShuttingDown() const
Overall (swift) application shutting down.
Definition: simulator.cpp:1257
void start()
Start listening for the simulator to start.
Definition: simulator.cpp:1266
void stop()
Stops listening.
Definition: simulator.cpp:1283
void check()
Check simulator availability.
Definition: simulator.cpp:1300
virtual QString backendInfo() const
Info about the backend system (if available)
Definition: simulator.cpp:1255
virtual void checkImpl()=0
Plugin specific implementation to check.
virtual void stopImpl()=0
Plugin specific implementation to stop listener.
size_type size() const
Returns number of elements in the collection.
Definition: collection.h:185
CCollection difference(const C &other) const
Returns a collection which contains all the elements from this collection which are not in the other ...
Definition: collection.h:278
CCollection intersection(const C &other) const
Returns a collection which is the intersection of this collection and another.
Definition: collection.h:268
iterator begin()
Returns iterator at the beginning of the collection.
Definition: collection.h:164
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
bool append(const QMetaObject::Connection &connection)
Add connection.
int disconnectAll()
Disconnect all.
static CCrashHandler * instance()
Get singleton instance.
void crashAndLogAppendInfo(const QString &info)
Append crash info.
Base class with a member CIdentifier to be inherited by a class which has an identity in the environm...
Definition: identifiable.h:24
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
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 isInThisThread(const QObject *toBeTested)
Is the current thread the object's thread?
Definition: threadutils.cpp:16
void setCapacityAndTokensToRefill(int numTokens)
Tokens/capacity if both are same.
Definition: tokenbucket.cpp:45
void setInterval(qint64 intervalMs)
Set the interval.
Definition: tokenbucket.h:40
bool tryConsume(int numTokens=1, qint64 msSinceEpoch=-1)
Try to consume a number of tokens.
Definition: tokenbucket.cpp:14
int getTokensPerSecond() const
Tokens per second.
Definition: tokenbucket.cpp:51
IRemoteAircraftProvider * provider()
Provider.
Definition: provider.h:73
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
void setCurrentUtcTime()
Set the current time as timestamp.
qint64 getTimeDifferenceAbsMs(qint64 compareTime) const
Time difference in ms.
void setTimeOffsetMs(qint64 offset)
Milliseconds to add to timestamp for interpolation.
void push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp=true, int maxElements=-1)
Insert as first element by keeping maxElements and the latest first.
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
Value object encapsulating information of an aircraft's situation.
const CAltitude & getGroundElevation() const
Elevation of the ground directly beneath.
void setCG(const physical_quantities::CLength &cg)
Set CG.
bool setGroundElevation(const aviation::CAltitude &altitude, GndElevationInfo info, bool transferred=false)
Elevation of the ground directly beneath at the given situation.
bool hasCallsign() const
Has corresponding callsign.
const CCallsign & getCallsign() const
Corresponding callsign.
const CAltitude & getAltitude() const
Get altitude.
Altitude as used in aviation, can be AGL or MSL altitude.
Definition: altitude.h:52
bool hasMeanSeaLevelValue() const
Non-NULL MSL value?
Definition: altitude.h:154
CAltitude withOffset(const CLength &offset) const
Altitude with offset.
Definition: altitude.cpp:61
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
bool isValid() const
Valid callsign?
Definition: callsign.cpp:359
Value object for a set of callsigns.
Definition: callsignset.h:26
QString getCallsignsAsString(bool sorted=false, const QString &separator=", ") const
Callsigns as string.
Definition: callsignset.cpp:43
QStringList getCallsignStrings(bool sorted=false) const
Get callsign string list.
Plane of same elevation, can be a single point or larger area (e.g. airport)
bool isNull() const
Existing value?
Geodetic coordinate, a position in 3D space relative to the reference geoid.
bool hasMSLGeodeticHeight() const
Geodetic height not null and aviation::CAltitude::MeanSeaLevel.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Class which can be directly used to access an.
Another client software.
Definition: client.h:27
Direct in memory access to client (network client) data.
Physical unit length (length)
Definition: length.h:18
void parseFromString(const QString &value)
Parse value from string.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
bool isZeroEpsilonConsidered() const
Quantity value <= epsilon.
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
void setModelString(const QString &modelString)
Model string.
CSimulatorInfo getSimulator() const
Simulator info.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
bool isCallsignEmpty() const
Callsign empty.
const physical_quantities::CLength & getCG() const
Get center of gravity.
QString getModelStringAndDbKey() const
Model string and DB key (if available)
bool hasModelString() const
Non empty model string?
Value object encapsulating a list of aircraft models.
const QString & generatingThreadName() const
Generating thread name.
bool isRenderingEnabled() const
Rendering enabled or all aircraft disabled?
bool isRestrictionChanged() const
Did the restriction flag change?
const swift::misc::aviation::CCallsignSet & getEnabledAircraftCallsignsByDistance() const
Callsigns by distance, only enabled aircraft.
void insert(const QString &modelString, const physical_quantities::CLength &cg)
Insert values we might want to update in the DB.
bool writeJsonToFile() const
Write to file.
bool setInterpolatorMode(InterpolatorMode mode)
Set interpolator mode.
void consolidateWithClient(const network::CClient &client)
Consolidate with a network client.
physical_quantities::CLength getMaxRenderedDistance() const
Max.distance for rendering.
bool isRenderingRestricted() const
Rendering enabled, but restricted.
Value object for interpolator and rendering per callsign.
SituationLog getLastSituationLog() const
Get last log.
Delegating class which can be directly used to access an.
bool updateOwnModel(const swift::misc::simulation::CAircraftModel &model)
Update model.
bool updateOwnSituation(const aviation::CAircraftSituation &situation)
Update own situation.
swift::misc::simulation::CAircraftModel getOwnAircraftModel() const
Own aircraft model.
CSimulatedAircraft getOwnAircraft() const
Own aircraft.
Class which can be directly used to access an.
aviation::CCallsignSet getAircraftInRangeCallsigns() const
Unique callsigns for aircraft in range.
bool isAircraftInRange(const aviation::CCallsign &callsign) const
Is aircraft in range?
CSimulatedAircraftList getAircraftInRange() const
All remote aircraft.
int getAircraftInRangeCount() const
Count remote aircraft.
CSimulatedAircraft getAircraftInRangeForCallsign(const aviation::CCallsign &callsign) const
Aircraft for callsign.
Comprehensive information of an aircraft.
bool hasModelString() const
Has model string?
bool hasCallsign() const
Callsign not empty, no further checks.
const aviation::CCallsign & getCallsign() const
Get callsign.
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
bool isEnabled() const
Enabled? Enable means it shall be displayed in the simulator.
QString getCallsignAsString() const
Get callsign.
const QString & getModelString() const
Get model string.
Value object encapsulating a list of aircraft.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool matchesAll(const CSimulatorInfo &otherInfo) const
Matches all simulators.
bool isSingleSimulator() const
Single simulator selected.
bool isUnspecified() const
Unspecified simulator.
void setSimulatorName(const QString &name)
Set simulator name.
void setSimulatorInstallationDirectory(const QString &fullFilePath)
Path where simulator is installed.
void setSwiftPluginName(const QString &name)
Set plugin name.
bool isEmulatedPlugin() const
Is this the emulated driver?
const QString & getIdentifier() const
Identifier.
const CSimulatorInfo & getSimulatorInfo() const
Simulator info object.
Direct in memory access to interpolation setup, normally implemented by simulator.
void setLogCallsign(bool log, const aviation::CCallsign &callsign)
Log/un-log given callsign.
CInterpolationAndRenderingSetupPerCallsign getInterpolationSetupPerCallsignOrDefault(const aviation::CCallsign &callsign) const
Get the setup for callsign, if not existing the global setup.
virtual bool setInterpolationSetupGlobal(const CInterpolationAndRenderingSetupGlobal &setup)
Set the global setup.
Direct threadsafe in memory access to own aircraft.
Direct thread safe in memory access to remote aircraft.
void setNewPluginInfo(const CSimulatorPluginInfo &info, const settings::CSimulatorSettings &settings, const CAircraftModel &defaultModel)
New plugin info and default model.
CSimulatorPluginInfo getSimulatorPluginInfo() const
Get the represented plugin.
bool rememberGroundElevation(const aviation::CCallsign &requestedForCallsign, bool likelyOnGroundElevation, const geo::ICoordinateGeodetic &elevationCoordinate, const physical_quantities::CLength &epsilon=geo::CElevationPlane::singlePointRadius())
Remember a given elevation.
bool insertCG(const physical_quantities::CLength &cg, const aviation::CCallsign &cs)
Insert or replace a CG.
int setMaxElevationsRemembered(int max)
Set number of elevations kept.
CSimulatorInfo getSimulatorInfo() const
Get the represented simulator.
QString getSimulatorName() const
Simulator name as set from the running simulator.
physical_quantities::CLength overriddenCGorDefault(const physical_quantities::CLength &defaultCG, const QString &modelString) const
Return the overridden CG value or the given default CG value.
CSimulatorSettings getSettings(const CSimulatorInfo &simulator) const
Settings per simulator.
Settings for simulator Driver independent parts (such as directories), also used in model loaders.
swift::misc::physical_quantities::CLength getRecordedGndRadius() const
Record GND values with radius.
CGSource
Where we get the CG (aka vertical offset) from.
bool isRecordOwnAircraftGnd() const
Record GND values (of own aircraft)
Allows to have specific utility functions for each simulator.
const CSimulatorSettings & getSimulatorSettings() const
The generic settings.
const QString & getSimulatorDirectoryOrDefault() const
Simulator directory or default path.
Classes interacting with the swift database (aka "datastore").
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Free functions in swift::misc.
QString className(const QObject *object)
Class name as from QMetaObject::className with namespace.
qint64 currentMSecsSinceEpoch()
bool openUrl(const QUrl &url)
QString absolutePath() const const
void clear()
bool contains(const Key &key) const const
bool remove(const Key &key)
T value(const Key &key) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setObjectName(QAnyStringView name)
bool isNull() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString toUpper() const const
QString join(QChar separator) const const
CaseInsensitive
QueuedConnection
QThread * currentThread()
QUrl fromLocalFile(const QString &localFile)
Log entry for parts interpolation.
QString toQString(const QString &separator={ " " }) const
To string.
Log entry for situation interpolation.
QString toQString(bool withSetup, bool withCurrentSituation, bool withElevation, bool withOtherPositions, bool withDeltaTimes, const QString &separator={ " " }) const
To string.
CInterpolationAndRenderingSetupPerCallsign usedSetup
used setup
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.
Definition: verify.h:38
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26