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 
541  {
542  CSimpleCommandParser::registerCommand({ ".drv fsuipc on|off", "enable/disable FSUIPC (if applicable)" });
543  }
544  }
545 
546  QString ISimulator::statusToString(SimulatorStatus status)
547  {
548  QStringList s;
549  if (status.testFlag(Unspecified)) { s << QStringLiteral("Unspecified"); }
550  if (status.testFlag(Disconnected)) { s << QStringLiteral("Disconnected"); }
551  if (status.testFlag(Connected)) { s << QStringLiteral("Connected"); }
552  if (status.testFlag(Simulating)) { s << QStringLiteral("Simulating"); }
553  if (status.testFlag(Paused)) { s << QStringLiteral("Paused"); }
554  return s.join(", ");
555  }
556 
557  bool ISimulator::isEqualLastSent(const CAircraftSituation &compare) const
558  {
559  Q_ASSERT_X(compare.hasCallsign(), Q_FUNC_INFO, "Need callsign");
560  if (!m_lastSentSituations.contains(compare.getCallsign())) { return false; }
561  if (compare.isNull()) { return false; }
562  return compare.equalPbhVectorAltitudeElevation(m_lastSentSituations.value(compare.getCallsign()));
563  // return compare.equalPbhVectorAltitude(m_lastSentSituations.value(compare.getCallsign()));
564  }
565 
566  bool ISimulator::isEqualLastSent(const CAircraftParts &compare, const CCallsign &callsign) const
567  {
568  if (callsign.isEmpty()) { return false; }
569  if (!m_lastSentParts.contains(callsign)) { return false; }
570  return compare.equalValues(m_lastSentParts.value(callsign));
571  }
572 
573  void ISimulator::rememberLastSent(const CAircraftSituation &sent)
574  {
575  // normally we should never end up without callsign, but it has happened in real world scenarios
576  // https://discordapp.com/channels/539048679160676382/568904623151382546/575712119513677826
577  const bool hasCs = sent.hasCallsign();
578  SWIFT_VERIFY_X(hasCs, Q_FUNC_INFO, "Need callsign");
579  if (!hasCs) { return; }
580  m_lastSentSituations.insert(sent.getCallsign(), sent);
581  }
582 
583  void ISimulator::rememberLastSent(const CAircraftParts &sent, const CCallsign &callsign)
584  {
585  // normally we should never end up without callsign, but it has happened in real world scenarios
586  // https://discordapp.com/channels/539048679160676382/568904623151382546/575712119513677826
587  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
588  if (callsign.isEmpty()) { return; }
589  m_lastSentParts.insert(callsign, sent);
590  }
591 
592  CAircraftSituationList ISimulator::getLastSentCanLikelySkipNearGroundInterpolation() const
593  {
594  const QList<CAircraftSituation> situations = m_lastSentSituations.values();
595  CAircraftSituationList skipped;
596  for (const CAircraftSituation &s : situations)
597  {
598  if (s.canLikelySkipNearGroundInterpolation()) { skipped.push_back(s); }
599  }
600  return skipped;
601  }
602 
603  bool ISimulator::isAnyConnectedStatus(SimulatorStatus status)
604  {
605  return (status.testFlag(Connected) || status.testFlag(Simulating) || status.testFlag(Paused));
606  }
607 
608  const CCallsign &ISimulator::getTestCallsign()
609  {
610  static const CCallsign cs("SWIFT");
611  return cs;
612  }
613 
614  ISimulator::ISimulator(const CSimulatorPluginInfo &pluginInfo, IOwnAircraftProvider *ownAircraftProvider,
615  IRemoteAircraftProvider *remoteAircraftProvider, IClientProvider *clientProvider,
616  QObject *parent)
617  : QObject(parent), COwnAircraftAware(ownAircraftProvider), CRemoteAircraftAware(remoteAircraftProvider),
619  CIdentifiable(this)
620  {
621  this->setObjectName("Simulator: " + pluginInfo.getIdentifier());
622  m_interpolationLogger.setObjectName("Logger: " + pluginInfo.getIdentifier());
623 
625 
626  // provider signals, hook up with remote aircraft provider
627  m_remoteAircraftProviderConnections.append(
628  CRemoteAircraftAware::provider()->connectRemoteAircraftProviderSignals(
629  this, // receiver must match object in bind
630  nullptr, nullptr,
631  [](const aviation::CCallsign &) {
632  /* currently not used, the calls are handled by context call logicallyRemoveRemoteAircraft*/
633  },
634  [this](const CAirspaceAircraftSnapshot &snapshot) {
635  this->rapOnRecalculatedRenderedAircraft(snapshot);
636  }));
637 
638  // swift data
639  if (sApp && sApp->hasWebDataServices())
640  {
645  }
647 
648  // provider
649  if (pluginInfo.isEmulatedPlugin() && !pluginInfo.getSimulatorInfo().isSingleSimulator())
650  {
651  // emulated driver with NO info yet
652  CLogMessage(this).info(u"Plugin '%1' with no simulator info yet, hope it will be set later")
653  << pluginInfo.getIdentifier();
654  }
655  else
656  {
657  // NORMAL CASE or plugin with info already set
658  this->setNewPluginInfo(pluginInfo, m_multiSettings.getSettings(pluginInfo.getSimulatorInfo()));
659  }
660 
661  // info data
664 
665  // model changed
667 
668  // info
669  CLogMessage(this).info(u"Initialized simulator driver: '%1'")
671  this->getSimulatorInfo().toQString());
672  }
673 
675  {
676  if (!snapshot.isValidSnapshot()) { return; }
677 
678  // for unrestricted values all add/remove actions are directly linked
679  // when changing back from restricted->unrestricted an one time update is required
680  if (!snapshot.isRestricted() && !snapshot.isRestrictionChanged()) { return; }
681 
682  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Needs to run in object thread");
683  Q_ASSERT_X(snapshot.generatingThreadName() != QThread::currentThread()->objectName(), Q_FUNC_INFO,
684  "Expect snapshot from background thread");
685 
686  // restricted snapshot values?
687  bool changed = false;
688  if (snapshot.isRenderingEnabled())
689  {
690  // make sure not to add aircraft again which are no longer in range
691  const CCallsignSet callsignsInRange = this->getAircraftInRangeCallsigns();
692  const CCallsignSet callsignsEnabledAndStillInRange =
693  snapshot.getEnabledAircraftCallsignsByDistance().intersection(callsignsInRange);
694  const CCallsignSet callsignsInSimulator(this->physicallyRenderedAircraft()); // state in simulator
695  const CCallsignSet callsignsToBeRemoved(callsignsInSimulator.difference(callsignsEnabledAndStillInRange));
696  const CCallsignSet callsignsToBeAdded(callsignsEnabledAndStillInRange.difference(callsignsInSimulator));
697  if (!callsignsToBeRemoved.isEmpty())
698  {
699  const int r = this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved);
700  changed = r > 0;
701  }
702 
703  if (!callsignsToBeAdded.isEmpty())
704  {
705  CSimulatedAircraftList aircraftToBeAdded(
706  this->getAircraftInRange().findByCallsigns(callsignsToBeAdded)); // thread safe copy
707  for (const CSimulatedAircraft &aircraft : aircraftToBeAdded)
708  {
709  Q_ASSERT_X(aircraft.isEnabled(), Q_FUNC_INFO, "Disabled aircraft detected as to be added");
710  Q_ASSERT_X(aircraft.hasModelString(), Q_FUNC_INFO, "Missing model string");
711  this->callPhysicallyAddRemoteAircraft(aircraft); // recalculate snapshot
712  changed = true;
713  }
714  }
715  }
716  else
717  {
718  // no rendering at all, we remove everything
719  const int r = this->physicallyRemoveAllRemoteAircraft();
720  changed = r > 0;
721  }
722 
723  // we have handled snapshot
724  if (changed) { emit this->airspaceSnapshotHandled(); }
725  }
726 
728  {
729  this->resetLastSentValues(callsign);
730  return true;
731  }
732 
734  {
735  if (callsigns.isEmpty()) { return 0; }
736  int removed = 0;
737  for (const CCallsign &callsign : callsigns)
738  {
739  this->callPhysicallyRemoveRemoteAircraft(callsign);
740  removed++;
741  }
742  return removed;
743  }
744 
746  {
747  // a default implementation, but normally overridden by the sims
748  const CCallsignSet callsigns = this->getAircraftInRangeCallsigns();
749 
750  // normally that would be already done in the specialized implementation
751  const int r = this->physicallyRemoveMultipleRemoteAircraft(callsigns);
752 
753  // leave no trash
754  this->clearAllRemoteAircraftData(); // remove all aircraft
755  return r;
756  }
757 
759  {
760  // void, can be overridden in specialized drivers
761  }
762 
764  {
765  // void, can be overridden in specialized drivers
766  }
767 
769  {
774  }
775 
777  bool likelyOnGroundElevation, const CElevationPlane &elevation,
778  const CLength &simulatorCG)
779  {
780  if (callsign.isEmpty()) { return; }
781  if (elevation.hasMSLGeodeticHeight())
782  {
783  const int aircraftCount = this->getAircraftInRangeCount();
785  aircraftCount *
786  3); // at least 3 elevations per aircraft, even better as not all are requesting elevations
787  this->rememberGroundElevation(callsign, likelyOnGroundElevation, elevation);
788  }
789 
790  const QString modelString = model.getModelString();
791  if (modelString.isEmpty()) { return; }
792 
793  // this value now is the simulator or overridden value
794  const CLength cgOvr = this->overriddenCGorDefault(simulatorCG, modelString);
795  if (!cgOvr.isNull() && !this->hasSameSimulatorCG(cgOvr, callsign))
796  {
797  // the value did change
798  const CSimulatorSettings::CGSource source =
800  if (source != CSimulatorSettings::CGFromDBOnly)
801  {
802  this->insertCG(cgOvr, modelString, callsign); // per model string and CG
803  }
804 
805  // here we know we have a valid model and CG did change
806  const CSimulatorInfo sim = this->getSimulatorInfo();
807  m_autoPublishing.insert(modelString,
808  simulatorCG); // still using simulator CG here, not the overridden value
809 
810  // if simulator did change, add as well
811  if (!model.getSimulator().matchesAll(sim))
812  {
813  m_autoPublishing.insert(modelString, this->getSimulatorInfo());
814  }
815  }
816  }
817 
818  void ISimulator::emitSimulatorCombinedStatus(SimulatorStatus oldStatus)
819  {
820  const SimulatorStatus newStatus = this->getSimulatorStatus();
821  if (oldStatus != newStatus)
822  {
823  // decouple, follow up of signal can include unloading
824  // simulator so this should happen strictly asynchronously (which is like forcing Qt::QueuedConnection)
825  QPointer<ISimulator> myself(this);
826  QTimer::singleShot(0, this, [=] {
827  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
828 
829  // now simulating
830  if (newStatus.testFlag(Simulating))
831  {
832  this->setUpdateAllRemoteAircraft(); // force an update of every remote aircraft
833  }
834 
835  emit this->simulatorStatusChanged(
836  newStatus); // only place where we should emit the signal, use emitSimulatorCombinedStatus to emit
837  });
838  }
839  }
840 
842  {
843  QPointer<ISimulator> myself(this);
844  QTimer::singleShot(5, this, [=] {
845  if (!myself) { return; }
847  });
848  }
849 
851  {
852  if (!IInterpolationSetupProvider::setInterpolationSetupGlobal(setup)) { return false; }
853  const bool r = setup.isRenderingRestricted();
854  const bool e = setup.isRenderingEnabled();
855 
856  if (!this->isShuttingDown())
857  {
858  CCrashHandler::instance()->crashAndLogAppendInfo(u"Rendering setup: " % setup.toQString(true));
859  }
861  return true;
862  }
863 
865  {
866  return m_loopbackSituations.value(callsign);
867  }
868 
870  {
871  bool modified = false;
872  const CAircraftModel reverseModel =
873  CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified);
874  return reverseModel;
875  }
876 
877  bool ISimulator::isUpdateAircraftLimited(qint64 timestamp)
878  {
879  if (!m_limitUpdateAircraft) { return false; }
880  const bool hasToken = m_limitUpdateAircraftBucket.tryConsume(1, timestamp);
881  return !hasToken;
882  }
883 
885  {
886  const bool limited = this->isUpdateAircraftLimited(startTime);
887  return limited;
888  }
889 
890  bool ISimulator::limitToUpdatesPerSecond(int numberPerSecond)
891  {
892  if (numberPerSecond < 1)
893  {
894  m_limitUpdateAircraft = false;
895  return false;
896  }
897 
898  int tokens = qRound(0.1 * numberPerSecond); // 100ms
899  do {
900  if (tokens >= 3)
901  {
903  break;
904  }
905  tokens = qRound(0.25 * numberPerSecond); // 250ms
906  if (tokens >= 3)
907  {
909  break;
910  }
911  tokens = qRound(0.5 * numberPerSecond); // 500ms
912  if (tokens >= 3)
913  {
915  break;
916  }
917  tokens = numberPerSecond;
919  }
920  while (false);
921 
923  m_limitUpdateAircraft = true;
924  return true;
925  }
926 
928  {
929  if (!m_limitUpdateAircraft) { return QStringLiteral("not limited"); }
930  static const QString limInfo("Limited %1 time(s) with %2/secs.");
932  }
933 
935  {
938  }
939 
941  {
942  m_lastSentParts.remove(callsign);
943  m_lastSentSituations.remove(callsign);
944  }
945 
947  {
948  this->disconnectFrom(); // disconnect from simulator
949  const bool saved = m_autoPublishing.writeJsonToFile(); // empty data are ignored
950  if (saved) { emit this->autoPublishDataWritten(this->getSimulatorInfo()); }
952  m_remoteAircraftProviderConnections.disconnectAll(); // disconnect signals from provider
953  }
954 
956  {
957  return this->isTestMode() || this->isAircraftInRange(callsign);
958  }
959 
961  {
962  m_averageFps = -1.0;
963  m_simTimeRatio = 1.0;
964  m_trackMilesShort = 0.0;
965  m_minutesLate = 0.0;
966  return true;
967  }
968 
970  {
971  if (this->isShuttingDown()) { return false; }
972  if (callsign.isEmpty()) { return false; }
973 
974  this->logicallyRemoveRemoteAircraft(callsign);
975  if (!this->isAircraftInRange(callsign)) { return false; }
976  const QPointer<ISimulator> myself(this);
977  QTimer::singleShot(2500, this, [=] {
978  if (myself.isNull()) { return; }
979  if (this->isShuttingDown()) { return; }
980  if (!this->isAircraftInRange(callsign)) { return; }
981  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
982  if (aircraft.isEnabled() && aircraft.hasModelString()) { this->logicallyAddRemoteAircraft(aircraft); }
983  });
984  return true;
985  }
986 
988  {
989  if (this->isShuttingDown()) { return false; }
990  return aircraft.isEnabled() ? this->physicallyAddRemoteAircraft(aircraft) :
992  }
993 
995  {
996  // we expect the new model "in aircraft"
997  // remove upfront, and then enable / disable again
998  if (this->isShuttingDown()) { return false; }
999  const CCallsign callsign = aircraft.getCallsign();
1000  if (!this->isPhysicallyRenderedAircraft(callsign)) { return false; }
1001  this->physicallyRemoveRemoteAircraft(callsign);
1002  // return this->changeRemoteAircraftEnabled(aircraft);
1003 
1004  const QPointer<ISimulator> myself(this);
1005  QTimer::singleShot(1000, this, [=] {
1006  if (!myself) { return; }
1007  if (this->isAircraftInRange(callsign)) { this->changeRemoteAircraftEnabled(aircraft); }
1008  });
1009  return true;
1010  }
1011 
1013  {
1014  CStatusMessageList msgs;
1015  if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; }
1017  {
1018  msgs.push_back(CStatusMessage(this).error(u"m_addAgainAircraftWhenRemoved not empty: '%1'")
1020  }
1021  return msgs;
1022  }
1023 
1025  const QString &details) const
1026  {
1027  static const QString msg("Interpolation ('%1'): '%2'");
1028  const QString m = msg.arg(callsign.asString(), status.toQString());
1029  if (details.isEmpty()) { return m; }
1030 
1031  static const QString addDetails(" details: '%1'");
1032  return m % addDetails.arg(details);
1033  }
1034 
1035  void ISimulator::finishUpdateRemoteAircraftAndSetStatistics(qint64 startTime, bool limited)
1036  {
1037  const qint64 now = QDateTime::currentMSecsSinceEpoch();
1038  const qint64 dt = now - startTime;
1043  static_cast<double>(m_statsUpdateAircraftTimeTotalMs) / static_cast<double>(m_statsUpdateAircraftRuns);
1046 
1047  if (!this->isUpdateAllRemoteAircraft(startTime)) { this->resetUpdateAllRemoteAircraft(); }
1048 
1051  {
1053  }
1054  if (limited) { m_statsUpdateAircraftLimited++; }
1055  }
1056 
1058  {
1059  Q_UNUSED(newModel)
1060  // can be overridden
1061  }
1062 
1064  {
1065  const bool updated = this->updateOwnSituation(situation);
1066 
1067  // do not use every situation, but every deltaMs and only on ground
1068  constexpr qint64 deltaMs = 5000;
1069  if (situation.isOnGround() && situation.getTimeDifferenceAbsMs(m_lastRecordedGndElevationMs) > deltaMs)
1070  {
1073  if (settings.isRecordOwnAircraftGnd())
1074  {
1075  const CSimulatedAircraft ownAircraft = this->getOwnAircraft();
1076  CAltitude elevation = situation.getGroundElevation();
1077  if (elevation.isNull())
1078  {
1079  // calculate elevation
1080  const CLength cg = ownAircraft.getModel().getCG();
1081  elevation = (cg.isNull() || situation.getAltitude().isNull()) ?
1082  CAltitude::null() :
1083  (situation.getAltitude().withOffset(cg * -1.0));
1084  }
1085 
1086  // own ground elevations
1087  if (elevation.hasMeanSeaLevelValue())
1088  {
1089  const CCallsign cs = situation.hasCallsign() ? situation.getCallsign() : ownAircraft.getCallsign();
1090  const CLength radius = settings.getRecordedGndRadius().isNull() ?
1091  CElevationPlane::singlePointRadius() :
1092  settings.getRecordedGndRadius();
1093  const CElevationPlane ep(situation, radius);
1094  const bool remembered = this->rememberGroundElevation(cs, situation.isOnGround(), ep, radius);
1095 
1096  if (CBuildConfig::isLocalDeveloperDebugBuild())
1097  {
1098  const bool invalid = situation.isOnGround() && elevation.isZeroEpsilonConsidered();
1099  SWIFT_AUDIT_X(!invalid, Q_FUNC_INFO, "On ground in water");
1100  }
1101  Q_UNUSED(remembered) // false means it was already in that cache, or something else is wrong
1102  }
1103  }
1104  }
1105  return updated;
1106  }
1107 
1109  {
1110  const CSimulatorInfo simulator = this->getSimulatorInfo();
1111  if (!simulator.isSingleSimulator()) { return {}; }
1112 
1113  CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
1114  return CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
1115  }
1116 
1118  {
1119  const CAircraftModel model = aircraft.getModel();
1120  if (!aircraft.hasCallsign())
1121  {
1122  CLogMessage(this).warning(u"Missing callsign for '%1'") << aircraft.getModelString();
1123  return false;
1124  }
1125  if (!model.hasModelString())
1126  {
1127  CLogMessage(this).warning(u"No model string for callsign '%1'") << aircraft.getCallsign();
1128  return false;
1129  }
1130  if (model.isCallsignEmpty())
1131  {
1132  CLogMessage(this).warning(u"No callsign for model of aircraft '%1'") << aircraft.getCallsign();
1133  return false;
1134  }
1135  return true;
1136  }
1137 
1139  {
1140  CLogMessage(this).info(u"Adding '%1' '%2' to '%3'")
1141  << aircraft.getCallsign() << aircraft.getModel().getModelStringAndDbKey()
1142  << this->getSimulatorInfo().toQString(true);
1143  }
1144 
1146  {
1149 
1150  static const QString sep("\n------\n");
1151  QString dm;
1152  if (s.tsCurrent > 0)
1153  {
1154  dm = u"Setup: " % s.usedSetup.toQString(true) % u"\n\n" % u"Situation: " %
1155  s.toQString(false, true, true, true, true, sep);
1156  }
1157  if (p.tsCurrent > 0) { dm += (dm.isEmpty() ? u"Parts: " : u"\n\nParts: ") % p.toQString(sep); }
1158  return dm;
1159  }
1160 
1161  void ISimulator::rapOnRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot)
1162  {
1163  if (!this->isConnected()) { return; }
1164  if (this->isShuttingDown()) { return; }
1165  this->onRecalculatedRenderedAircraft(snapshot);
1166  }
1167 
1168  void ISimulator::callPhysicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft)
1169  {
1170  m_statsPhysicallyAddedAircraft++;
1171  this->physicallyAddRemoteAircraft(remoteAircraft);
1172  }
1173 
1174  void ISimulator::callPhysicallyRemoveRemoteAircraft(const CCallsign &remoteCallsign)
1175  {
1176  this->clearData(remoteCallsign);
1177  this->physicallyRemoveRemoteAircraft(remoteCallsign);
1178  }
1179 
1180  void ISimulator::displayLoggedSituationInSimulator(const CCallsign &cs, bool stopLogging, int times)
1181  {
1182  if (cs.isEmpty()) { return; }
1183  if (this->isShuttingDown()) { return; }
1185  const bool logsCs = setup.logInterpolation();
1186  if (!logsCs) { return; }
1187 
1188  stopLogging = stopLogging || !this->isSimulating(); // stop when sim was stopped
1189  stopLogging = stopLogging && logsCs;
1190  if (!stopLogging && times < 1) { return; }
1191 
1192  const bool inRange = this->getAircraftInRangeCallsigns().contains(cs);
1193  if (!stopLogging && !inRange) { return; }
1194  if (stopLogging && (times < 1 || !inRange))
1195  {
1196  this->setLogCallsign(false, cs);
1197  return;
1198  }
1199 
1200  const QString dm = this->latestLoggedDataFormatted(cs);
1201  if (!dm.isEmpty()) { this->displayStatusMessage(CStatusMessage(this).info(dm)); }
1202 
1203  const int t = CMathUtils::randomInteger(4500, 5500); // makes sure not always using the same time difference
1204  const QPointer<ISimulator> myself(this);
1205  QTimer::singleShot(t, this, [=] {
1206  if (!myself || myself->isShuttingDown()) { return; }
1207  this->displayLoggedSituationInSimulator(cs, stopLogging, times - 1);
1208  });
1209  }
1210 
1212  {
1213  CAircraftModel model = this->getOwnAircraftModel();
1214  model.setModelString(modelString);
1216  }
1217 
1219  {
1220  if (!model.hasModelString()) { return; }
1221  if (this->isShuttingDown()) { return; }
1222  Q_ASSERT_X(sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web services");
1223 
1224  if (this->getOwnAircraftModel() != model)
1225  {
1226  if (CDatabaseUtils::hasDbAircraftData())
1227  {
1229  const bool updated = this->updateOwnModel(newModel); // update in provider (normally the context)
1230  if (updated) { emit this->ownAircraftModelChanged(this->getOwnAircraftModel()); }
1231  }
1232  else
1233  {
1234  // we wait for the data
1236  [=] { this->reverseLookupAndUpdateOwnAircraftModel(model); });
1237  }
1238  }
1239  }
1240 
1242  {
1243  this->setObjectName("ISimulatorListener:" + info.toQString());
1244 
1245  // stop listener after it reports simulator ready
1246  bool s =
1248  Q_ASSERT_X(s, Q_FUNC_INFO, "connect failed");
1249 
1250  if (sApp)
1251  {
1252  s = connect(sApp, &CApplication::aboutToShutdown, this, &ISimulatorListener::onAboutToShutdown,
1254  Q_ASSERT_X(s, Q_FUNC_INFO, "connect failed");
1255  }
1256 
1257  Q_UNUSED(s)
1258  }
1259 
1261 
1262  bool ISimulatorListener::isShuttingDown() const { return (!sApp || sApp->isShuttingDown() || m_aboutToShutdown); }
1263 
1264  void ISimulatorListener::onAboutToShutdown()
1265  {
1266  if (!m_aboutToShutdown) { return; }
1267  m_aboutToShutdown = true;
1268  this->stop();
1269  }
1270 
1272  {
1273  if (m_isRunning) { return; }
1274  if (!CThreadUtils::isInThisThread(this))
1275  {
1276  // call in correct thread
1277  QPointer<ISimulatorListener> myself(this);
1278  QTimer::singleShot(0, this, [=] {
1279  if (myself) { this->start(); }
1280  });
1281  return;
1282  }
1283 
1284  m_isRunning = true;
1285  this->startImpl();
1286  }
1287 
1289  {
1290  if (!m_isRunning) { return; }
1291  if (!CThreadUtils::isInThisThread(this))
1292  {
1293  // call in correct thread
1294  QPointer<ISimulatorListener> myself(this);
1295  QTimer::singleShot(0, this, [=] {
1296  if (myself) { this->stop(); }
1297  });
1298  return;
1299  }
1300 
1301  this->stopImpl();
1302  m_isRunning = false;
1303  }
1304 
1306  {
1307  if (!m_isRunning) { return; }
1308  if (!CThreadUtils::isInThisThread(this))
1309  {
1310  // call in correct thread
1311  QPointer<ISimulatorListener> myself(this);
1312  QTimer::singleShot(0, this, [=] {
1313  if (myself) { this->check(); }
1314  });
1315  return;
1316  }
1317 
1318  this->checkImpl();
1319  }
1320 } // namespace swift::core
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
static constexpr bool isCompiledWithFsuipcSupport()
with FSUIPC support?
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:969
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:934
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:850
bool updateOwnSituationAndGroundElevation(const swift::misc::aviation::CAircraftSituation &situation)
Update own aircraft position and if suitable use it to update ground elevation.
Definition: simulator.cpp:1063
virtual void onOwnModelChanged(const swift::misc::simulation::CAircraftModel &newModel)
Own model has been changed.
Definition: simulator.cpp:1057
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:927
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:890
virtual void onSwiftDbAllDataRead()
When swift DB data are read.
Definition: simulator.cpp:758
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:776
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:727
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:955
void finishUpdateRemoteAircraftAndSetStatistics(qint64 startTime, bool limited=false)
Update stats and flags.
Definition: simulator.cpp:1035
swift::misc::aviation::CAircraftSituationList getLoopbackSituations(const swift::misc::aviation::CCallsign &callsign) const
The traced loopback situations.
Definition: simulator.cpp:864
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:1218
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:841
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:674
virtual void unload()
Driver will be unloaded.
Definition: simulator.cpp:946
swift::misc::simulation::CAircraftModelList getModelSet() const
Get the model set.
Definition: simulator.cpp:1108
bool isUpdateAircraftLimited(qint64 timestamp=-1)
Limit reached (max number of updates by token bucket if enabled)
Definition: simulator.cpp:877
virtual swift::misc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const
Debug function to check state after all aircraft have been removed.
Definition: simulator.cpp:1012
virtual bool disconnectFrom()
Disconnect from simulator.
Definition: simulator.cpp:960
bool validateModelOfAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft) const
Validate if model has callsign and such.
Definition: simulator.cpp:1117
void emitSimulatorCombinedStatus(SimulatorStatus oldStatus=Unspecified)
Emit the combined status.
Definition: simulator.cpp:818
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:763
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:745
void logAddingAircraftModel(const swift::misc::simulation::CSimulatedAircraft &aircraft) const
Unified qeeing aircraft message.
Definition: simulator.cpp:1138
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:884
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:733
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:768
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:869
QString latestLoggedDataFormatted(const swift::misc::aviation::CCallsign &cs) const
The latest logged data formatted.
Definition: simulator.cpp:1145
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:994
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:987
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:1024
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:1241
virtual bool isShuttingDown() const
Overall (swift) application shutting down.
Definition: simulator.cpp:1262
void start()
Start listening for the simulator to start.
Definition: simulator.cpp:1271
void stop()
Stops listening.
Definition: simulator.cpp:1288
void check()
Check simulator availability.
Definition: simulator.cpp:1305
virtual QString backendInfo() const
Info about the backend system (if available)
Definition: simulator.cpp:1260
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:72
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