swift
simulatorflightgear.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "simulatorflightgear.h"
5 
6 #include <math.h>
7 
8 #include <QColor>
9 #include <QDBusServiceWatcher>
10 #include <QPointer>
11 #include <QString>
12 #include <QTimer>
13 #include <QtGlobal>
14 
15 #include "dbus/dbus.h"
16 #include "fgswiftbusserviceproxy.h"
17 #include "fgswiftbustrafficproxy.h"
18 #include "qcompilerdetection.h"
19 
20 #include "config/buildconfig.h"
21 #include "core/aircraftmatcher.h"
28 #include "misc/aviation/altitude.h"
29 #include "misc/aviation/callsign.h"
31 #include "misc/aviation/heading.h"
32 #include "misc/aviation/livery.h"
34 #include "misc/dbusserver.h"
36 #include "misc/geo/latitude.h"
37 #include "misc/geo/longitude.h"
38 #include "misc/iterator.h"
39 #include "misc/logmessage.h"
42 #include "misc/pq/angle.h"
43 #include "misc/pq/frequency.h"
44 #include "misc/pq/length.h"
45 #include "misc/pq/pressure.h"
46 #include "misc/pq/speed.h"
47 #include "misc/pq/temperature.h"
51 #include "misc/verify.h"
54 #include "misc/weather/windlayer.h"
56 
57 using namespace swift::config;
58 using namespace swift::misc;
59 using namespace swift::misc::aviation;
60 using namespace swift::misc::network;
61 using namespace swift::misc::physical_quantities;
62 using namespace swift::misc::simulation;
63 using namespace swift::misc::geo;
64 using namespace swift::misc::simulation;
65 using namespace swift::misc::weather;
66 using namespace swift::core;
67 
68 namespace
69 {
70  inline const QString &fgswiftbusServiceName()
71  {
72  static const QString name("org.swift-project.fgswiftbus");
73  return name;
74  }
75 } // namespace
76 
77 namespace swift::simplugin::flightgear
78 {
79  int FGSWIFTBUS_API_VERSION = -1;
80  QList<int> incompatibleVersions = { 1, 2 };
81  CSimulatorFlightgear::CSimulatorFlightgear(const CSimulatorPluginInfo &info,
82  IOwnAircraftProvider *ownAircraftProvider,
83  IRemoteAircraftProvider *remoteAircraftProvider,
84  IClientProvider *clientProvider, QObject *parent)
85  : CSimulatorPluginCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent)
86  {
87  m_watcher = new QDBusServiceWatcher(this);
89  m_watcher->addWatchedService(fgswiftbusServiceName());
90  m_watcher->setObjectName("QDBusServiceWatcher");
92  &CSimulatorFlightgear::onDBusServiceUnregistered, Qt::QueuedConnection);
93 
94  m_fastTimer.setObjectName(this->objectName().append(":m_fastTimer"));
95  m_slowTimer.setObjectName(this->objectName().append(":m_slowTimer"));
96  m_pendingAddedTimer.setObjectName(this->objectName().append(":m_pendingAddedTimer"));
97  connect(&m_fastTimer, &QTimer::timeout, this, &CSimulatorFlightgear::fastTimerTimeout);
98  connect(&m_slowTimer, &QTimer::timeout, this, &CSimulatorFlightgear::slowTimerTimeout);
99  connect(&m_pendingAddedTimer, &QTimer::timeout, this, &CSimulatorFlightgear::addNextPendingAircraft);
100  m_fastTimer.start(100);
101  m_slowTimer.start(1000);
102  m_pendingAddedTimer.start(5000);
103 
104  this->setDefaultModel(
105  { "FG c172p", CAircraftModel::TypeModelMatchingDefaultModel, "C172", CAircraftIcaoCode("C172", "L1P") });
106  this->resetFlightgearData();
107  }
108 
110 
112  {
113  if (!this->isConnected()) { return; }
114 
115  // will call disconnect from
116  CSimulatorPluginCommon::unload();
117  delete m_watcher;
118  m_watcher = nullptr;
119  }
120 
122  {
123  return QStringLiteral("Add-time: %1ms/%2ms").arg(m_statsAddCurrentTimeMs).arg(m_statsAddMaxTimeMs);
124  }
125 
127  {
128  m_statsAddMaxTimeMs = -1;
129  m_statsAddCurrentTimeMs = -1;
130  }
131 
133  {
134  if (callsign.isEmpty() || !m_flightgearAircraftObjects.contains(callsign)) { return CStatusMessageList(); }
136  this->getInterpolationSetupConsolidated(callsign, false);
137  return m_flightgearAircraftObjects[callsign].getInterpolationMessages(setup.getInterpolatorMode());
138  }
139 
141  const CAircraftParts &parts)
142  {
143  if (this->isShuttingDownOrDisconnected()) { return false; }
144  if (!m_trafficProxy) { return false; }
145  if (!m_flightgearAircraftObjects.contains(callsign)) { return false; }
146 
147  int u = 0;
148  if (!situation.isNull())
149  {
150  PlanesPositions planesPositions;
151  planesPositions.push_back(situation);
152  m_trafficProxy->setPlanesPositions(planesPositions);
153  u++;
154  }
155 
156  if (parts.isNull() && flightgear::FGSWIFTBUS_API_VERSION >= 2)
157  {
158  PlanesSurfaces surfaces;
159  surfaces.push_back(callsign, parts);
160  m_trafficProxy->setPlanesSurfaces(surfaces);
161  u++;
162  }
163  return u > 0;
164  }
165 
167  {
168  m_aircraftAddedFailed.clear();
169  CSimulatorPluginCommon::clearAllRemoteAircraftData();
170  }
171 
172  // convert flightgear squawk mode to swift squawk mode
173  CTransponder::TransponderMode xpdrMode(int transponderMode, bool ident)
174  {
175  if (ident) { return CTransponder::StateIdent; }
176  if (transponderMode == 0 || transponderMode == 1 || transponderMode == 2) { return CTransponder::StateStandby; }
177  return CTransponder::ModeC;
178  }
179 
180  // convert swift squawk mode to flightgear squawk mode
181  int xpdrMode(CTransponder::TransponderMode mode) { return mode == CTransponder::StateStandby ? 1 : 4; }
182 
183  void CSimulatorFlightgear::fastTimerTimeout()
184  {
185  if (!this->isShuttingDownOrDisconnected())
186  {
187  m_serviceProxy->getOwnAircraftSituationData(&m_flightgearData);
188  m_serviceProxy->getOwnAircraftVelocityData(&m_flightgearData);
189  m_serviceProxy->isPausedAsync(&m_simulatorPaused);
190  m_serviceProxy->getCom1ActiveKhzAsync(&m_flightgearData.com1ActiveKhz);
191  m_serviceProxy->getCom1StandbyKhzAsync(&m_flightgearData.com1StandbyKhz);
192  m_serviceProxy->getCom2ActiveKhzAsync(&m_flightgearData.com2ActiveKhz);
193  m_serviceProxy->getCom2StandbyKhzAsync(&m_flightgearData.com2StandbyKhz);
194  m_serviceProxy->getTransponderCodeAsync(&m_flightgearData.xpdrCode);
195  m_serviceProxy->getTransponderModeAsync(&m_flightgearData.xpdrMode);
196  m_serviceProxy->getTransponderIdentAsync(&m_flightgearData.xpdrIdent);
197  m_serviceProxy->getAllWheelsOnGroundAsync(&m_flightgearData.onGroundAll);
198  m_serviceProxy->getGroundElevationAsync(&m_flightgearData.groundElevation);
199  m_serviceProxy->getCom1VolumeAsync(&m_flightgearData.volumeCom1);
200  m_serviceProxy->getCom2VolumeAsync(&m_flightgearData.volumeCom2);
201 
202  CAircraftSituation situation;
203  situation.setPosition({ m_flightgearData.latitudeDeg, m_flightgearData.longitudeDeg, 0 });
204  situation.setAltitude({ m_flightgearData.altitudeFt, CAltitude::MeanSeaLevel, CLengthUnit::ft() });
205  situation.setPressureAltitude({ m_flightgearData.pressureAltitudeFt, CAltitude::MeanSeaLevel,
206  CAltitude::PressureAltitude, CLengthUnit::ft() });
207  situation.setHeading({ m_flightgearData.trueHeadingDeg, CHeading::True, CAngleUnit::deg() });
208  situation.setPitch({ m_flightgearData.pitchDeg, CAngleUnit::deg() });
209  situation.setBank({ m_flightgearData.rollDeg, CAngleUnit::deg() });
210  situation.setGroundSpeed({ m_flightgearData.groundspeedKts, CSpeedUnit::kts() });
211  situation.setGroundElevation(
212  CAltitude(m_flightgearData.groundElevation, CAltitude::MeanSeaLevel, CLengthUnit::m()),
213  CAircraftSituation::FromProvider);
214 
215  if (!m_simulatorPaused)
216  {
217  situation.setVelocity({ m_flightgearData.velocityXMs, m_flightgearData.velocityYMs,
218  m_flightgearData.velocityZMs, CSpeedUnit::m_s(),
219  m_flightgearData.pitchRateRadPerSec, m_flightgearData.rollRateRadPerSec,
220  m_flightgearData.yawRateRadPerSec, CAngleUnit::rad(), CTimeUnit::s() });
221  }
222  else
223  {
224  situation.setVelocity(
225  { 0.0, 0.0, 0.0, CSpeedUnit::m_s(), 0.0, 0.0, 0.0, CAngleUnit::rad(), CTimeUnit::s() });
226  }
227 
228  // Updates
229  // Do not update ICAO codes, as this overrides reverse lookups
230  // updateOwnIcaoCodes(m_flightgearData.aircraftIcaoCode, CAirlineIcaoCode());
231  this->updateOwnSituationAndGroundElevation(situation);
232 
233  // defaults
234  CSimulatedAircraft myAircraft(getOwnAircraft());
235  CComSystem com1(myAircraft.getCom1System()); // set defaults
236  CComSystem com2(myAircraft.getCom2System());
237  CTransponder transponder(myAircraft.getTransponder());
238 
239  // updates
240  com1.setFrequencyActive(CFrequency(m_flightgearData.com1ActiveKhz, CFrequencyUnit::kHz()));
241  com1.setFrequencyStandby(CFrequency(m_flightgearData.com1StandbyKhz, CFrequencyUnit::kHz()));
242  com1.setVolumeReceive(qRound(m_flightgearData.volumeCom1 * 100));
243  const bool changedCom1 = myAircraft.getCom1System() != com1;
244 
245  com2.setFrequencyActive(CFrequency(m_flightgearData.com2ActiveKhz, CFrequencyUnit::kHz()));
246  com2.setFrequencyStandby(CFrequency(m_flightgearData.com2StandbyKhz, CFrequencyUnit::kHz()));
247  com2.setVolumeReceive(qRound(m_flightgearData.volumeCom2 * 100));
248  const bool changedCom2 = myAircraft.getCom2System() != com2;
249 
250  transponder = CTransponder::getStandardTransponder(
251  m_flightgearData.xpdrCode, xpdrMode(m_flightgearData.xpdrMode, m_flightgearData.xpdrIdent));
252  const bool changedXpr = (myAircraft.getTransponder() != transponder);
253 
254  if (changedCom1 || changedCom2 || changedXpr)
255  {
256  this->updateCockpit(com1, com2, transponder, identifier());
257  }
258  }
259  }
260 
261  void CSimulatorFlightgear::slowTimerTimeout()
262  {
263  if (!this->isShuttingDownOrDisconnected())
264  {
265  m_serviceProxy->getAircraftModelPathAsync(
266  &m_flightgearData.aircraftModelPath); // this is NOT the model string
267  m_serviceProxy->getAircraftIcaoCodeAsync(&m_flightgearData.aircraftIcaoCode);
268  m_serviceProxy->getBeaconLightsOnAsync(&m_flightgearData.beaconLightsOn);
269  m_serviceProxy->getLandingLightsOnAsync(&m_flightgearData.landingLightsOn);
270  m_serviceProxy->getNavLightsOnAsync(&m_flightgearData.navLightsOn);
271  m_serviceProxy->getStrobeLightsOnAsync(&m_flightgearData.strobeLightsOn);
272  m_serviceProxy->getTaxiLightsOnAsync(&m_flightgearData.taxiLightsOn);
273  m_serviceProxy->getFlapsDeployRatioAsync(&m_flightgearData.flapsReployRatio);
274  m_serviceProxy->getGearDeployRatioAsync(&m_flightgearData.gearReployRatio);
275  m_serviceProxy->getEngineN1PercentageAsync(&m_flightgearData.enginesN1Percentage);
276  m_serviceProxy->getSpeedBrakeRatioAsync(&m_flightgearData.speedBrakeRatio);
277 
278  CAircraftEngineList engines;
279  for (int engineNumber = 0; engineNumber < m_flightgearData.enginesN1Percentage.size(); ++engineNumber)
280  {
281  // Engine number start counting at 1
282  // We consider the engine running when N1 is bigger than 5 %
283  CAircraftEngine engine { engineNumber + 1,
284  m_flightgearData.enginesN1Percentage.at(engineNumber) > 5.0 };
285  engines.push_back(engine);
286  }
287 
288  CAircraftParts parts { { m_flightgearData.strobeLightsOn, m_flightgearData.landingLightsOn,
289  m_flightgearData.taxiLightsOn, m_flightgearData.beaconLightsOn,
290  m_flightgearData.navLightsOn, false },
291  m_flightgearData.gearReployRatio > 0,
292  static_cast<int>(m_flightgearData.flapsReployRatio * 100),
293  m_flightgearData.speedBrakeRatio > 0.5,
294  engines,
295  m_flightgearData.onGroundAll };
296 
297  this->updateOwnParts(parts);
298  this->requestRemoteAircraftDataFromFlightgear();
299 
300  CCallsignSet invalid;
301  for (CFlightgearMPAircraft &flightgearAircraft : m_flightgearAircraftObjects)
302  {
303  // Update remote aircraft to have the latest transponder modes, codes etc.
304  const CCallsign cs = flightgearAircraft.getCallsign();
305  const CSimulatedAircraft simulatedAircraft = this->getAircraftInRangeForCallsign(cs);
306  if (!simulatedAircraft.hasCallsign())
307  {
308  if (!cs.isEmpty()) { invalid.insert(cs); }
309  continue;
310  }
311  flightgearAircraft.setSimulatedAircraft(simulatedAircraft);
312  }
313 
314  int i = 0;
315 
316  for (const CCallsign &cs : invalid) { this->triggerRemoveAircraft(cs, ++i * 100); }
317  }
318  }
319 
320  bool CSimulatorFlightgear::isConnected() const { return m_serviceProxy && m_trafficProxy; }
321 
323  {
324  if (isConnected()) { return true; }
325  QString dbusAddress = m_fgswiftbusServerSetting.getThreadLocal().getDBusServerAddress();
326 
327  if (CDBusServer::isSessionOrSystemAddress(dbusAddress))
328  {
329  m_dBusConnection = QDBusConnection::sessionBus();
330  m_dbusMode = Session;
331  }
332  else if (CDBusServer::isQtDBusAddress(dbusAddress))
333  {
334  m_dBusConnection = QDBusConnection::connectToPeer(dbusAddress, "fgswiftbus");
335  if (!m_dBusConnection.isConnected()) { return false; }
336  m_dbusMode = P2P;
337  }
338 
339  m_serviceProxy = new CFGSwiftBusServiceProxy(m_dBusConnection, this);
340  m_trafficProxy = new CFGSwiftBusTrafficProxy(m_dBusConnection, this);
341 
342  const bool s = m_dBusConnection.connect(QString(), DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL, "Disconnected", this,
343  SLOT(onDBusServiceUnregistered()));
344  Q_ASSERT(s);
345  if (!m_serviceProxy->isValid() || !m_trafficProxy->isValid())
346  {
347  this->disconnectFrom();
348  return false;
349  }
350 
351  emitOwnAircraftModelChanged(m_serviceProxy->getAircraftModelPath(), m_serviceProxy->getAircraftModelFilename(),
352  m_serviceProxy->getAircraftLivery(), m_serviceProxy->getAircraftIcaoCode(),
353  m_serviceProxy->getAircraftModelString(), m_serviceProxy->getAircraftName(),
354  m_serviceProxy->getAircraftDescription());
355  setSimulatorDetails("Flightgear", {}, "");
357  &CSimulatorFlightgear::emitOwnAircraftModelChanged);
358  connect(m_trafficProxy, &CFGSwiftBusTrafficProxy::simFrame, this, &CSimulatorFlightgear::updateRemoteAircraft);
360  &CSimulatorFlightgear::onRemoteAircraftAdded);
362  &CSimulatorFlightgear::onRemoteAircraftAddingFailed);
363  if (m_watcher) { m_watcher->setConnection(m_dBusConnection); }
364  m_trafficProxy->removeAllPlanes();
366 
367  this->initSimulatorInternals();
368  return true;
369  }
370 
372  {
373  if (!this->isConnected()) { return true; } // avoid emit if already disconnected
374  this->disconnectFromDBus();
375  if (m_watcher) { m_watcher->setConnection(m_dBusConnection); }
376  delete m_serviceProxy;
377  delete m_trafficProxy;
378  m_serviceProxy = nullptr;
379  m_trafficProxy = nullptr;
381  return true;
382  }
383 
384  void CSimulatorFlightgear::onDBusServiceUnregistered()
385  {
386  if (!m_serviceProxy) { return; }
387  CLogMessage(this).info(u"FG DBus service unregistered");
388 
389  if (m_dbusMode == P2P) { m_dBusConnection.disconnectFromPeer(m_dBusConnection.name()); }
390  m_dBusConnection = QDBusConnection { "default" };
391  if (m_watcher) { m_watcher->setConnection(m_dBusConnection); }
392  delete m_serviceProxy;
393  delete m_trafficProxy;
394  m_serviceProxy = nullptr;
395  m_trafficProxy = nullptr;
397  }
398 
399  void CSimulatorFlightgear::emitOwnAircraftModelChanged(const QString &path, const QString &filename,
400  const QString &livery, const QString &icao,
401  const QString &modelString, const QString &name,
402  const QString &description)
403  {
404  CAircraftModel model(modelString, CAircraftModel::TypeOwnSimulatorModel, CSimulatorInfo::XPLANE, name,
405  description, icao);
406  if (!livery.isEmpty()) { model.setModelString(model.getModelString()); }
407  model.setFileName(path + "/" + filename);
408 
410  }
411 
413  {
414  // No assert here as status message may come because of network problems
415  if (this->isShuttingDownOrDisconnected()) { return; }
416 
417  // avoid infinite recursion in case this function is called due to a message caused by this very function
418  static bool isInFunction = false;
419  if (isInFunction) { return; }
420  isInFunction = true;
421 
422  m_serviceProxy->addTextMessage("swift: " + message.getMessage());
423  isInFunction = false;
424  }
425 
427  {
428  if (this->isShuttingDownOrDisconnected()) { return; }
429  m_serviceProxy->addTextMessage(message.getSenderCallsign().toQString() + ": " + message.getMessage());
430  }
431 
433  {
434  return m_flightgearAircraftObjects.contains(callsign);
435  }
436 
438  const CIdentifier &originator)
439  {
440  if (originator == this->identifier()) { return false; }
441  if (this->isShuttingDownOrDisconnected()) { return false; }
442 
443  auto com1 = CComSystem::getCom1System({ m_flightgearData.com1ActiveKhz, CFrequencyUnit::kHz() },
444  { m_flightgearData.com1StandbyKhz, CFrequencyUnit::kHz() });
445  auto com2 = CComSystem::getCom2System({ m_flightgearData.com2ActiveKhz, CFrequencyUnit::kHz() },
446  { m_flightgearData.com2StandbyKhz, CFrequencyUnit::kHz() });
447  auto xpdr = CTransponder::getStandardTransponder(
448  m_flightgearData.xpdrCode, xpdrMode(m_flightgearData.xpdrMode, m_flightgearData.xpdrIdent));
449  if (aircraft.hasChangedCockpitData(com1, com2, xpdr))
450  {
451  m_flightgearData.com1ActiveKhz =
452  aircraft.getCom1System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz());
453  m_flightgearData.com1StandbyKhz =
454  aircraft.getCom1System().getFrequencyStandby().valueInteger(CFrequencyUnit::kHz());
455  m_flightgearData.com2ActiveKhz =
456  aircraft.getCom2System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz());
457  m_flightgearData.com2StandbyKhz =
458  aircraft.getCom2System().getFrequencyStandby().valueInteger(CFrequencyUnit::kHz());
459  m_flightgearData.xpdrCode = aircraft.getTransponderCode();
460  m_flightgearData.xpdrMode = xpdrMode(aircraft.getTransponderMode());
461  m_serviceProxy->setCom1ActiveKhz(m_flightgearData.com1ActiveKhz);
462  m_serviceProxy->setCom1StandbyKhz(m_flightgearData.com1StandbyKhz);
463  m_serviceProxy->setCom2ActiveKhz(m_flightgearData.com2ActiveKhz);
464  m_serviceProxy->setCom2StandbyKhz(m_flightgearData.com2StandbyKhz);
465  m_serviceProxy->setTransponderCode(m_flightgearData.xpdrCode);
466  m_serviceProxy->setTransponderMode(m_flightgearData.xpdrMode);
467 
468  m_serviceProxy
469  ->cancelAllPendingAsyncCalls(); // in case there is already a reply with some old data incoming
470  return true;
471  }
472  return false;
473  }
474 
476  {
477  if (originator == this->identifier()) { return false; }
478  if (this->isShuttingDownOrDisconnected()) { return false; }
479 
481  Q_UNUSED(selcal)
482 
483  return false;
484  }
485 
487  {
488  if (this->isShuttingDownOrDisconnected()) { return false; }
489 
490  // entry checks
491  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread");
492  Q_ASSERT_X(!newRemoteAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "empty callsign");
493  Q_ASSERT_X(newRemoteAircraft.hasModelString(), Q_FUNC_INFO, "missing model string");
494 
495  // crosscheck if still a valid aircraft
496  // it can happen that aircraft has been removed, timed out ...
497  if (!this->isAircraftInRangeOrTestMode(newRemoteAircraft.getCallsign()))
498  {
499  // next cycle will be called by callbacks or timer
500  CLogMessage(this).warning(u"Aircraft '%1' no longer in range, will not add")
501  << newRemoteAircraft.getCallsign();
502  return false;
503  }
504 
505  if (this->canAddAircraft())
506  {
507  // no aircraft pending, add
508  this->logAddingAircraftModel(newRemoteAircraft);
509  const qint64 now = QDateTime::currentMSecsSinceEpoch();
510  m_addingInProgressAircraft.insert(newRemoteAircraft.getCallsign(), now);
511  const QString callsign = newRemoteAircraft.getCallsign().asString();
512  CAircraftModel aircraftModel = newRemoteAircraft.getModel();
513  if (aircraftModel.getCallsign() != newRemoteAircraft.getCallsign())
514  {
515  CLogMessage(this).warning(u"Model for '%1' has no callsign, maybe using a default model") << callsign;
516  aircraftModel.setCallsign(callsign);
517  }
518  const QString livery = aircraftModel.getLivery().getCombinedCode();
519  m_trafficProxy->addPlane(callsign, aircraftModel.getFileName(),
520  newRemoteAircraft.getAircraftIcaoCode().getDesignator(),
521  newRemoteAircraft.getAirlineIcaoCode().getDesignator(), livery);
522 
523  PlanesPositions pos;
524  pos.push_back(newRemoteAircraft.getSituation());
525  m_trafficProxy->setPlanesPositions(pos);
526  if (flightgear::FGSWIFTBUS_API_VERSION >= 2)
527  {
528  PlanesSurfaces surfaces;
529  surfaces.push_back(newRemoteAircraft.getCallsign(), newRemoteAircraft.getParts());
530  m_trafficProxy->setPlanesSurfaces(surfaces);
531  }
532  }
533  else
534  {
535  // add in queue
536  m_pendingToBeAddedAircraft.replaceOrAdd(newRemoteAircraft);
537  }
538  return true;
539  }
540 
542  {
543  if (this->isShuttingDownOrDisconnected()) { return false; }
544 
545  // only remove from sim
546  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "wrong thread");
547  if (callsign.isEmpty()) { return false; } // can happen if an object is not an aircraft
548 
549  // really remove from simulator
550  if (!m_flightgearAircraftObjects.contains(callsign) && !m_pendingToBeAddedAircraft.containsCallsign(callsign) &&
551  !m_addingInProgressAircraft.contains(callsign))
552  {
553  // not existing aircraft
554  return false;
555  }
556 
557  // mark in provider
558  const bool updated = this->updateAircraftRendered(callsign, false);
559  if (updated)
560  {
561  if (m_flightgearAircraftObjects.contains(callsign))
562  {
563  const CFlightgearMPAircraft &flightgearAircraft = m_flightgearAircraftObjects[callsign];
564  CSimulatedAircraft aircraft(flightgearAircraft.getAircraft());
565  aircraft.setRendered(false);
566  emit this->aircraftRenderingChanged(aircraft);
567  }
568  else if (m_pendingToBeAddedAircraft.containsCallsign(callsign))
569  {
570  CSimulatedAircraft aircraft = m_pendingToBeAddedAircraft.findFirstByCallsign(callsign);
571  aircraft.setRendered(false);
572  emit this->aircraftRenderingChanged(aircraft);
573  }
574  }
575 
576  if (m_addingInProgressAircraft.contains(callsign))
577  {
578  // we are just about to add that aircraft
579  QPointer<CSimulatorFlightgear> myself(this);
580  QTimer::singleShot(TimeoutAdding, this, [=] {
581  if (!myself) { return; }
582  m_addingInProgressAircraft.remove(callsign); // remove as "in progress"
583  this->physicallyRemoveRemoteAircraft(callsign); // and remove from sim. if it was added in the mean time
584  });
585  return false;
586  }
587 
588  m_trafficProxy->removePlane(callsign.asString());
589  m_flightgearAircraftObjects.remove(callsign);
590  m_pendingToBeAddedAircraft.removeByCallsign(callsign);
591 
592  // bye
593  return CSimulatorPluginCommon::physicallyRemoveRemoteAircraft(callsign);
594  }
595 
597  {
598  if (!this->isConnected()) { return 0; }
599  m_pendingToBeAddedAircraft.clear();
600  m_addingInProgressAircraft.clear();
601  return CSimulatorPluginCommon::physicallyRemoveAllRemoteAircraft();
602  }
603 
605  {
606  return this->getAircraftInRange().findByRendered(true).getCallsigns(); // just a poor workaround
607  }
608 
609  void CSimulatorFlightgear::updateRemoteAircraft()
610  {
611  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread");
612 
613  const int remoteAircraftNo = this->getAircraftInRangeCount();
614  if (remoteAircraftNo < 1) { return; }
615 
616  // values used for position and parts
618  const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
619 
620  // interpolation for all remote aircraft
621  PlanesPositions planesPositions;
622  PlanesSurfaces planesSurfaces;
623  PlanesTransponders planesTransponders;
624 
625  uint32_t aircraftNumber = 0;
626  const bool updateAllAircraft = this->isUpdateAllRemoteAircraft(currentTimestamp);
627  const CCallsignSet callsignsInRange = this->getAircraftInRangeCallsigns();
628  for (const CFlightgearMPAircraft &flightgearAircraft : std::as_const(m_flightgearAircraftObjects))
629  {
630  const CCallsign callsign(flightgearAircraft.getCallsign());
631  const bool hasCallsign = !callsign.isEmpty();
632  if (!hasCallsign)
633  {
634  // does not make sense to continue here
635  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "missing callsign");
636  continue;
637  }
638 
639  // skip no longer in range
640  if (!callsignsInRange.contains(callsign)) { continue; }
641 
642  planesTransponders.callsigns.push_back(callsign.asString());
643  planesTransponders.codes.push_back(flightgearAircraft.getAircraft().getTransponderCode());
644  CTransponder::TransponderMode transponderMode = flightgearAircraft.getAircraft().getTransponderMode();
645  planesTransponders.idents.push_back(transponderMode == CTransponder::StateIdent);
646  planesTransponders.modeCs.push_back(transponderMode == CTransponder::ModeC);
647 
648  // setup
650  this->getInterpolationSetupConsolidated(callsign, updateAllAircraft);
651 
652  // interpolated situation/parts
653  const CInterpolationResult result =
654  flightgearAircraft.getInterpolation(currentTimestamp, setup, aircraftNumber++);
656  {
657  const CAircraftSituation interpolatedSituation(result);
658 
659  // update situation
660  if (updateAllAircraft || !this->isEqualLastSent(interpolatedSituation))
661  {
662  this->rememberLastSent(interpolatedSituation);
663  planesPositions.push_back(interpolatedSituation);
664  }
665  }
666  else
667  {
668  CLogMessage(this).warning(
669  this->getInvalidSituationLogMessage(callsign, result.getInterpolationStatus()));
670  }
671 
672  const CAircraftParts parts(result);
673  if (result.getPartsStatus().isSupportingParts() || parts.getPartsDetails() == CAircraftParts::GuessedParts)
674  {
675  if (updateAllAircraft || !this->isEqualLastSent(parts, callsign))
676  {
677  this->rememberLastSent(parts, callsign);
678  planesSurfaces.push_back(flightgearAircraft.getCallsign(), parts);
679  }
680  }
681 
682  } // all callsigns
683 
684  if (!planesTransponders.isEmpty() && flightgear::FGSWIFTBUS_API_VERSION >= 2)
685  {
686  m_trafficProxy->setPlanesTransponders(planesTransponders);
687  }
688 
689  if (!planesPositions.isEmpty())
690  {
691  if (CBuildConfig::isLocalDeveloperDebugBuild())
692  {
693  SWIFT_VERIFY_X(planesPositions.hasSameSizes(), Q_FUNC_INFO, "Mismatching sizes");
694  }
695  m_trafficProxy->setPlanesPositions(planesPositions);
696  }
697 
698  if (!planesSurfaces.isEmpty() && flightgear::FGSWIFTBUS_API_VERSION >= 2)
699  {
700  m_trafficProxy->setPlanesSurfaces(planesSurfaces);
701  }
702 
703  // stats
704  this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp);
705  }
706 
707  void CSimulatorFlightgear::requestRemoteAircraftDataFromFlightgear()
708  {
709  if (this->isShuttingDownOrDisconnected()) { return; }
710 
711  // It is not required to request all elevations and CGs, but only for aircraft "near ground relevant"
712  // - we could use the elevation cache and CG cache to decide if we need to request
713  // - if an aircraft is on ground but not moving, we do not need to request elevation if we already have it (it
714  // will not change
715  CCallsignSet callsigns = m_flightgearAircraftObjects.getAllCallsigns();
717  callsigns.remove(remove);
718  if (!callsigns.isEmpty()) { this->requestRemoteAircraftDataFromFlightgear(callsigns); }
719  }
720 
721  void CSimulatorFlightgear::requestRemoteAircraftDataFromFlightgear(const CCallsignSet &callsigns)
722  {
723  if (callsigns.isEmpty()) { return; }
724  if (!m_trafficProxy || this->isShuttingDown()) { return; }
725  const QStringList csStrings = callsigns.getCallsignStrings();
726  QPointer<CSimulatorFlightgear> myself(this);
727  m_trafficProxy->getRemoteAircraftData(
728  csStrings,
729  [=](const QStringList &callsigns, const QDoubleList &latitudesDeg, const QDoubleList &longitudesDeg,
730  const QDoubleList &elevationsMeters, const QDoubleList &verticalOffsetsMeters) {
731  if (!myself) { return; }
732  this->updateRemoteAircraftFromSimulator(callsigns, latitudesDeg, longitudesDeg, elevationsMeters,
733  verticalOffsetsMeters);
734  });
735  }
736 
737  void CSimulatorFlightgear::triggerRequestRemoteAircraftDataFromFlightgear(const CCallsignSet &callsigns)
738  {
739  if (callsigns.isEmpty()) { return; }
740  QPointer<CSimulatorFlightgear> myself(this);
741  QTimer::singleShot(0, this, [=] {
742  if (!myself) { return; }
743  this->requestRemoteAircraftDataFromFlightgear(callsigns);
744  });
745  }
746 
747  void CSimulatorFlightgear::updateRemoteAircraftFromSimulator(const QStringList &callsigns,
748  const QDoubleList &latitudesDeg,
749  const QDoubleList &longitudesDeg,
750  const QDoubleList &elevationsMeters,
751  const QDoubleList &verticalOffsetsMeters)
752  {
753  const int size = callsigns.size();
754 
755  // we skip if we are not near ground
756  if (CBuildConfig::isLocalDeveloperDebugBuild())
757  {
758  Q_ASSERT_X(elevationsMeters.size() == size, Q_FUNC_INFO, "Wrong elevations");
759  Q_ASSERT_X(latitudesDeg.size() == size, Q_FUNC_INFO, "Wrong latitudesDeg");
760  Q_ASSERT_X(longitudesDeg.size() == size, Q_FUNC_INFO, "Wrong longitudesDeg");
761  Q_ASSERT_X(verticalOffsetsMeters.size() == size, Q_FUNC_INFO, "Wrong CG");
762  }
763 
764  const CCallsignSet logCallsigns = this->getLogCallsigns();
765  for (int i = 0; i < size; i++)
766  {
767  const bool emptyCs = callsigns[i].isEmpty();
768  SWIFT_VERIFY_X(!emptyCs, Q_FUNC_INFO, "Need callsign");
769  if (emptyCs) { continue; }
770  const CCallsign cs(callsigns[i]);
771  if (!m_flightgearAircraftObjects.contains(cs)) { continue; }
772  const CFlightgearMPAircraft fgAircraft = m_flightgearAircraftObjects[cs];
773  SWIFT_VERIFY_X(fgAircraft.hasCallsign(), Q_FUNC_INFO, "Need callsign");
774  if (!fgAircraft.hasCallsign()) { continue; }
775 
776  CElevationPlane elevation = CElevationPlane::null();
777  if (!std::isnan(elevationsMeters[i]))
778  {
779  const CAltitude elevationAlt = CAltitude(elevationsMeters[i], CLengthUnit::m(), CLengthUnit::ft());
780  elevation = CElevationPlane(CLatitude(latitudesDeg[i], CAngleUnit::deg()),
781  CLongitude(longitudesDeg[i], CAngleUnit::deg()), elevationAlt,
782  CElevationPlane::singlePointRadius());
783  }
784 
785  const double cgValue = verticalOffsetsMeters[i]; // XP offset is swift CG
786  const CLength cg =
787  std::isnan(cgValue) ? CLength::null() : CLength(cgValue, CLengthUnit::m(), CLengthUnit::ft());
788 
789  // if we knew "on ground" here we could set it as parameter of rememberElevationAndSimulatorCG
790  this->rememberElevationAndSimulatorCG(cs, fgAircraft.getAircraftModel(), false, elevation, cg);
791 
792  // loopback
793  if (logCallsigns.contains(cs)) { this->addLoopbackSituation(cs, elevation, cg); }
794  }
795  }
796 
797  void CSimulatorFlightgear::disconnectFromDBus()
798  {
799  if (m_dBusConnection.isConnected())
800  {
801  if (m_trafficProxy) { m_trafficProxy->cleanup(); }
802 
803  if (m_dbusMode == P2P) { QDBusConnection::disconnectFromPeer(m_dBusConnection.name()); }
804  else { QDBusConnection::disconnectFromBus(m_dBusConnection.name()); }
805  }
806  m_dBusConnection = QDBusConnection { "default" };
807  }
808 
809  void CSimulatorFlightgear::onRemoteAircraftAdded(const QString &callsign)
810  {
811  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
812  if (callsign.isEmpty()) { return; }
813  const CCallsign cs(callsign);
814  CSimulatedAircraft addedRemoteAircraft = this->getAircraftInRangeForCallsign(cs);
815 
816  // statistics
817  bool wasPending = false;
818  if (m_addingInProgressAircraft.contains(cs))
819  {
820  wasPending = true;
821  const qint64 wasStartedMs = m_addingInProgressAircraft.value(cs);
822  const qint64 deltaTimeMs = QDateTime::currentMSecsSinceEpoch() - wasStartedMs;
823  m_statsAddCurrentTimeMs = deltaTimeMs;
824  if (deltaTimeMs > m_statsAddMaxTimeMs) { m_statsAddMaxTimeMs = deltaTimeMs; }
825  m_addingInProgressAircraft.remove(cs);
826  }
827 
828  if (!addedRemoteAircraft.hasCallsign())
829  {
830  CLogMessage(this).warning(u"Aircraft '%1' no longer in range, will be removed") << callsign;
831  this->triggerRemoveAircraft(cs, TimeoutAdding);
832  return;
833  }
834 
835  CLogMessage(this).info(u"Added aircraft '%1'") << callsign;
836  if (!wasPending)
837  {
838  // timeout?
839  // slow adding?
840  CLogMessage(this).warning(u"Added callsign '%1' was not in progress anymore. Timeout?") << callsign;
841  }
842 
843  const bool rendered = true;
844  addedRemoteAircraft.setRendered(rendered);
845  this->updateAircraftRendered(cs, rendered);
846  this->triggerRequestRemoteAircraftDataFromFlightgear(cs);
847  this->triggerAddNextPendingAircraft();
848 
849  Q_ASSERT_X(addedRemoteAircraft.hasCallsign(), Q_FUNC_INFO, "No callsign");
850  Q_ASSERT_X(addedRemoteAircraft.getCallsign() == cs, Q_FUNC_INFO, "No callsign");
851  m_flightgearAircraftObjects.insert(cs,
852  CFlightgearMPAircraft(addedRemoteAircraft, this, &m_interpolationLogger));
853  emit this->aircraftRenderingChanged(addedRemoteAircraft);
854  }
855 
857  const swift::misc::aviation::CCallsign &callsign)
858  {
859  if (this->isShuttingDownOrDisconnected()) { return false; }
860  if (reference.isNull()) { return false; }
861 
862  CCoordinateGeodetic pos(reference);
863  if (!pos.hasMSLGeodeticHeight())
864  {
865  // testing showed: height has an influence on the returned result
866  // - the most accurate value seems to be returned if the height is close to the elevation
867  // - in normal scenarios there is no much difference of the results if 0 is used
868  // - in Lukla (9200ft MSL) the difference between 0 and 9200 is around 1ft
869  // - in the LOWW scenario using 50000ft MSL results in around 3ft too low elevation
870  static const CAltitude alt(0, CAltitude::MeanSeaLevel, CLengthUnit::ft());
871  pos.setGeodeticHeight(alt);
872  }
873 
874  using namespace std::placeholders;
875  auto callback = std::bind(&CSimulatorFlightgear::callbackReceivedRequestedElevation, this, _1, _2, false);
876 
877  // Request
878  m_trafficProxy->getElevationAtPosition(callsign, pos.latitude().value(CAngleUnit::deg()),
879  pos.longitude().value(CAngleUnit::deg()),
880  pos.geodeticHeight().value(CLengthUnit::m()), callback);
881  emit this->requestedElevation(callsign);
882  return true;
883  }
884 
885  void CSimulatorFlightgear::onRemoteAircraftAddingFailed(const QString &callsign)
886  {
887  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
888  if (callsign.isEmpty()) { return; }
889  const CCallsign cs(callsign);
890  CSimulatedAircraft failedRemoteAircraft = this->getAircraftInRangeForCallsign(cs);
891 
892  if (failedRemoteAircraft.hasCallsign())
893  {
894  CLogMessage(this).warning(u"Adding aircraft failed: '%1'") << callsign;
895  failedRemoteAircraft.setRendered(false);
896  }
897  else
898  {
899  CLogMessage(this).warning(u"Adding '%1' failed, but aircraft no longer in range, will be removed")
900  << callsign;
901  }
902 
903  const bool wasPending = (static_cast<int>(m_addingInProgressAircraft.remove(cs)) > 0);
904  Q_UNUSED(wasPending)
905 
906  if (failedRemoteAircraft.hasCallsign() && !m_aircraftAddedFailed.containsCallsign(cs))
907  {
908  m_aircraftAddedFailed.push_back(failedRemoteAircraft);
909  m_pendingToBeAddedAircraft.replaceOrAdd(failedRemoteAircraft); // try a second time
910  }
911  this->triggerAddNextPendingAircraft();
912  }
913 
914  void CSimulatorFlightgear::addNextPendingAircraft()
915  {
916  if (m_pendingToBeAddedAircraft.isEmpty()) { return; } // no more pending
917 
918  // housekeeping
919  this->detectTimeoutAdding();
920 
921  // check if can add
922  if (!this->canAddAircraft()) { return; }
923 
924  // next add cycle
925  const CSimulatedAircraft newRemoteAircraft = m_pendingToBeAddedAircraft.front();
926  m_pendingToBeAddedAircraft.pop_front();
927  CLogMessage(this).info(u"Adding next pending aircraft '%1', pending %2, in progress %3")
928  << newRemoteAircraft.getCallsignAsString() << m_pendingToBeAddedAircraft.size()
929  << m_addingInProgressAircraft.size();
930  this->physicallyAddRemoteAircraft(newRemoteAircraft);
931  }
932 
933  void CSimulatorFlightgear::triggerAddNextPendingAircraft()
934  {
935  QPointer<CSimulatorFlightgear> myself(this);
936  QTimer::singleShot(100, this, [=] {
937  if (!myself) { return; }
938  this->addNextPendingAircraft();
939  });
940  }
941 
942  int CSimulatorFlightgear::detectTimeoutAdding()
943  {
944  if (m_addingInProgressAircraft.isEmpty()) { return 0; }
945  const qint64 timeout = QDateTime::currentMSecsSinceEpoch() + TimeoutAdding;
946  CCallsignSet timeoutCallsigns;
947  const QList<CCallsign> addingCallsigns = m_addingInProgressAircraft.keys();
948  for (const CCallsign &cs : addingCallsigns)
949  {
950  if (m_addingInProgressAircraft.value(cs) < timeout) { continue; }
951  timeoutCallsigns.push_back(cs);
952  }
953 
954  for (const CCallsign &cs : std::as_const(timeoutCallsigns))
955  {
956  m_addingInProgressAircraft.remove(cs);
957  CLogMessage(this).warning(u"Adding for '%1' timed out") << cs.asString();
958  }
959 
960  return timeoutCallsigns.size();
961  }
962 
963  void CSimulatorFlightgear::triggerRemoveAircraft(const CCallsign &callsign, qint64 deferMs)
964  {
965  QPointer<CSimulatorFlightgear> myself(this);
966  QTimer::singleShot(deferMs, this, [=] {
967  if (!myself) { return; }
968  this->physicallyRemoveRemoteAircraft(callsign);
969  });
970  }
971 
972  QPair<qint64, qint64> CSimulatorFlightgear::minMaxTimestampsAddInProgress() const
973  {
974  static const QPair<qint64, qint64> empty(-1, -1);
975  if (m_addingInProgressAircraft.isEmpty()) { return empty; }
976  const QList<qint64> ts = m_addingInProgressAircraft.values();
977  const auto mm = std::minmax_element(ts.constBegin(), ts.constEnd());
978  return QPair<qint64, qint64>(*mm.first, *mm.second);
979  }
980 
981  bool CSimulatorFlightgear::canAddAircraft() const
982  {
983  if (this->getModelSet().isEmpty()) { return false; }
984  if (m_addingInProgressAircraft.isEmpty()) { return true; }
985 
986  // check
987  const qint64 now = QDateTime::currentMSecsSinceEpoch();
988  const QPair<qint64, qint64> tsMM = this->minMaxTimestampsAddInProgress();
989  const qint64 deltaLatest = now - tsMM.second;
990  const bool canAdd = (deltaLatest > TimeoutAdding);
991  return canAdd;
992  }
993 
995  IOwnAircraftProvider *ownAircraftProvider,
996  IRemoteAircraftProvider *remoteAircraftProvider,
997  IClientProvider *clientProvider)
998  {
999  return new CSimulatorFlightgear(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, this);
1000  }
1001 
1003  : ISimulatorListener(info)
1004  {
1005  constexpr int QueryInterval = 5 * 1000; // 5 seconds
1006  m_timer.setInterval(QueryInterval);
1007  m_timer.setObjectName(this->objectName().append(":m_timer"));
1008  connect(&m_timer, &QTimer::timeout, this, &CSimulatorFlightgearListener::checkConnection);
1009  }
1010 
1012 
1014 
1016  {
1017  if (!m_timer.isActive()) { return; }
1018  if (this->isShuttingDown()) { return; }
1019 
1020  m_timer.start(); // restart because we will check just now
1022  QTimer::singleShot(0, this, [=] {
1023  if (!myself) { return; }
1024  checkConnection();
1025  });
1026  }
1027 
1028  void CSimulatorFlightgearListener::checkConnection()
1029  {
1030  if (this->isShuttingDown()) { return; }
1031  Q_ASSERT_X(!CThreadUtils::thisIsMainThread(), Q_FUNC_INFO, "Expect to run in background");
1032 
1033  QString dbusAddress = m_fgSswiftBusServerSetting.getThreadLocal().getDBusServerAddress();
1034  if (CDBusServer::isSessionOrSystemAddress(dbusAddress)) { checkConnectionViaSessionBus(); }
1035  else if (CDBusServer::isQtDBusAddress(dbusAddress)) { checkConnectionViaPeer(dbusAddress); }
1036  }
1037 
1038  void CSimulatorFlightgearListener::checkConnectionViaSessionBus()
1039  {
1040  m_conn = QDBusConnection::sessionBus();
1041  if (!m_conn.isConnected())
1042  {
1043  m_conn.disconnectFromBus(m_conn.name());
1044  return;
1045  }
1046  checkConnectionCommon();
1047  m_conn.disconnectFromBus(m_conn.name());
1048  }
1049 
1050  void CSimulatorFlightgearListener::checkConnectionViaPeer(const QString &address)
1051  {
1052  m_conn = QDBusConnection::connectToPeer(address, "fgswiftbus");
1053  if (!m_conn.isConnected())
1054  {
1055  // This is required to cleanup the connection in QtDBus
1056  m_conn.disconnectFromPeer(m_conn.name());
1057  return;
1058  }
1059  checkConnectionCommon();
1060  m_conn.disconnectFromPeer(m_conn.name());
1061  }
1062 
1063  void CSimulatorFlightgearListener::checkConnectionCommon()
1064  {
1065  CFGSwiftBusServiceProxy service(m_conn);
1066  CFGSwiftBusTrafficProxy traffic(m_conn);
1067 
1068  bool result = service.isValid() && traffic.isValid();
1069  if (!result) { return; }
1070 
1071  flightgear::FGSWIFTBUS_API_VERSION = service.getVersionNumber();
1072 
1073  if (flightgear::incompatibleVersions.contains(flightgear::FGSWIFTBUS_API_VERSION))
1074  {
1075  CLogMessage(this).error(u"This version of swift is not compatible with this Flightgear version. For "
1076  u"further information check http://wiki.flightgear.org/Swift.");
1077  return;
1078  }
1079  if (!traffic.initialize())
1080  {
1081  CLogMessage(this).error(u"Connection to FGSwiftBus successful, but could not initialize FGSwiftBus.");
1082  return;
1083  }
1084 
1085  const MultiplayerAcquireInfo info = traffic.acquireMultiplayerPlanes();
1086  if (!info.hasAcquired)
1087  {
1088  const QString owner =
1089  info.owner.trimmed().isEmpty() ? QStringLiteral("Some/this plugin/application") : info.owner.trimmed();
1090  CLogMessage(this).error(
1091  u"Connection to FGSwiftBus successful, but could not acquire multiplayer planes. '%1' has acquired "
1092  u"them already. Disable '%2' or remove it if not required and reload FGSwiftBus.")
1093  << owner << owner.toLower();
1094  return;
1095  }
1096 
1098  }
1099 
1100  void CSimulatorFlightgearListener::serviceRegistered(const QString &serviceName)
1101  {
1102  if (serviceName == fgswiftbusServiceName()) { emit simulatorStarted(getPluginInfo()); }
1103  m_conn.disconnectFromBus(m_conn.name());
1104  }
1105 
1106  void CSimulatorFlightgearListener::fgSwiftBusServerSettingChanged()
1107  {
1108  this->stop();
1109  this->start();
1110  }
1111 } // namespace swift::simplugin::flightgear
Interface to a simulator.
Definition: simulator.h:59
virtual void callbackReceivedRequestedElevation(const swift::misc::geo::CElevationPlane &plane, const swift::misc::aviation::CCallsign &callsign, bool isWater)
A requested elevation has been received.
Definition: simulator.cpp:254
bool addLoopbackSituation(const swift::misc::aviation::CAircraftSituation &situation)
Add a loopback situation if logging is enabled.
Definition: simulator.cpp:180
bool isEqualLastSent(const swift::misc::aviation::CAircraftSituation &compare) const
Equal to last sent situation.
Definition: simulator.cpp:551
bool updateOwnSituationAndGroundElevation(const swift::misc::aviation::CAircraftSituation &situation)
Update own aircraft position and if suitable use it to update ground elevation.
Definition: simulator.cpp:1057
bool isUpdateAllRemoteAircraft(qint64 currentTimestamp=-1) const
Do update all remote aircraft?
Definition: simulator.cpp:212
virtual bool isShuttingDown() const
Is overall (swift) application shutting down.
Definition: simulator.h:211
void requestedElevation(const swift::misc::aviation::CCallsign &callsign)
Requested elevation, call pending.
bool m_updateRemoteAircraftInProgress
currently updating remote aircraft
Definition: simulator.h:556
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:770
bool isAircraftInRangeOrTestMode(const swift::misc::aviation::CCallsign &callsign) const
Test version aware version of isAircraftInRange.
Definition: simulator.cpp:949
void finishUpdateRemoteAircraftAndSetStatistics(qint64 startTime, bool limited=false)
Update stats and flags.
Definition: simulator.cpp:1029
void reverseLookupAndUpdateOwnAircraftModel(const swift::misc::simulation::CAircraftModel &model)
Set own model.
Definition: simulator.cpp:1212
swift::misc::aviation::CAircraftSituationList getLastSentCanLikelySkipNearGroundInterpolation() const
Last sent situations.
Definition: simulator.cpp:586
swift::misc::simulation::CAircraftModelList getModelSet() const
Get the model set.
Definition: simulator.cpp:1102
void emitSimulatorCombinedStatus(SimulatorStatus oldStatus=Unspecified)
Emit the combined status.
Definition: simulator.cpp:812
void logAddingAircraftModel(const swift::misc::simulation::CSimulatedAircraft &aircraft) const
Unified qeeing aircraft message.
Definition: simulator.cpp:1132
swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign getInterpolationSetupConsolidated(const swift::misc::aviation::CCallsign &callsign, bool forceFullUpdate) const
Consolidate setup with other data like from swift::misc::simulation::IRemoteAircraftProvider.
Definition: simulator.cpp:237
swift::misc::simulation::CInterpolationLogger m_interpolationLogger
log.interpolation
Definition: simulator.h:579
void aircraftRenderingChanged(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Aircraft rendering changed.
virtual bool isShuttingDownOrDisconnected() const
Shutting down or disconnected?
Definition: simulator.h:214
virtual void initSimulatorInternals()
Init the internals info from the simulator.
Definition: simulator.cpp:762
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:1018
void rememberLastSent(const swift::misc::aviation::CAircraftSituation &sent)
Remember as last sent.
Definition: simulator.cpp:567
Interface to a simulator listener.
Definition: simulator.h:630
const swift::misc::simulation::CSimulatorPluginInfo & getPluginInfo() const
Corresponding info.
Definition: simulator.h:638
void simulatorStarted(const swift::misc::simulation::CSimulatorPluginInfo &info)
Emitted when the listener discovers the simulator running.
virtual bool isShuttingDown() const
Overall (swift) application shutting down.
Definition: simulator.cpp:1256
void start()
Start listening for the simulator to start.
Definition: simulator.cpp:1265
void stop()
Stops listening.
Definition: simulator.cpp:1282
const T & getThreadLocal() const
Read the current value.
Definition: valuecache.h:400
size_type size() const
Returns number of elements in the collection.
Definition: collection.h:185
void remove(const T &object)
Efficient remove using the find and erase of the implementation container. Typically O(log n).
Definition: collection.h:307
iterator insert(const_iterator hint, const T &value)
For compatibility with std::inserter.
Definition: collection.h:199
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
iterator push_back(const T &value)
Synonym for insert.
Definition: collection.h:238
const CIdentifier & identifier() const
Get identifier.
Definition: identifiable.h:27
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
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 & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
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
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void replaceOrAdd(const T &original, const T &replacement)
Replace elements matching the given element. If there is no match, push the new element on the end.
Definition: sequence.h:521
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
void pop_front()
Removes an element at the front of the sequence.
Definition: sequence.h:374
Streamable status message, e.g.
QString getMessage() const
Message.
Status messages, e.g. from Core -> GUI.
Value object encapsulating information about aircraft's engines.
Value object encapsulating a list of aircraft engines.
Value object for ICAO classification.
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
bool isNull() const
NULL parts object?
Value object encapsulating information of an aircraft's situation.
void setPressureAltitude(const CAltitude &altitude)
Set pressure altitude.
void setGroundSpeed(const physical_quantities::CSpeed &groundspeed)
Set ground speed.
bool setGroundElevation(const aviation::CAltitude &altitude, GndElevationInfo info, bool transferred=false)
Elevation of the ground directly beneath at the given situation.
void setBank(const physical_quantities::CAngle &bank)
Set bank (angle)
void setHeading(const CHeading &heading)
Set heading.
void setAltitude(const CAltitude &altitude)
Set altitude.
void setPitch(const physical_quantities::CAngle &pitch)
Set pitch.
virtual bool isNull() const
Null situation.
void setVelocity(const CAircraftVelocity &velocity)
Set 6DOF velocity.
void setPosition(const geo::CCoordinateGeodetic &position)
Set position.
const QString & getDesignator() const
Get airline, e.g. "DLH".
Altitude as used in aviation, can be AGL or MSL altitude.
Definition: altitude.h:52
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Value object for a set of callsigns.
Definition: callsignset.h:26
QStringList getCallsignStrings(bool sorted=false) const
The callsign strings.
Definition: callsignset.cpp:37
COM system (aka "radio")
Definition: comsystem.h:37
const QString & getCombinedCode() const
Combined code.
Definition: livery.h:71
swift::misc::physical_quantities::CFrequency getFrequencyStandby() const
Standby frequency.
Definition: modulator.cpp:36
swift::misc::physical_quantities::CFrequency getFrequencyActive() const
Active frequency.
Definition: modulator.cpp:30
Value object for SELCAL.
Definition: selcal.h:31
OBJ findFirstByCallsign(const CCallsign &callsign, const OBJ &ifNotFound={}) const
Find the first aircraft by callsign, if none return given one.
int removeByCallsign(const CCallsign &callsign)
Remove all objects with callsign.
swift::misc::aviation::CCallsignSet getCallsigns() const
All callsigns.
bool containsCallsign(const CCallsign &callsign) const
Contains callsign?
virtual CLatitude latitude() const
Latitude.
virtual const aviation::CAltitude & geodeticHeight() const
Height, ellipsoidal or geodetic height (used in GPS)
void setGeodeticHeight(const aviation::CAltitude &height)
Set height (ellipsoidal or geodetic height)
virtual CLongitude longitude() const
Longitude.
Plane of same elevation, can be a single point or larger area (e.g. airport)
Geodetic coordinate, a position in 3D space relative to the reference geoid.
bool hasMSLGeodeticHeight() const
Geodetic height not null and aviation::CAltitude::MeanSeaLevel.
virtual bool isNull() const
Is null, means vector x, y, z == 0.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Value object encapsulating information of a text message.
Definition: textmessage.h:31
const QString & getMessage() const
Get message.
Definition: textmessage.h:78
const aviation::CCallsign & getSenderCallsign() const
Get callsign (from)
Definition: textmessage.h:54
Direct in memory access to client (network client) data.
Physical unit length (length)
Definition: length.h:18
Specialized class for distance units (meter, foot, nautical miles).
Definition: units.h:95
int valueInteger(MU unit) const
As integer value.
double value(MU unit) const
Value in given unit.
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
const aviation::CLivery & getLivery() const
Get livery.
void setCallsign(const aviation::CCallsign &callsign)
Corresponding callsign if applicable.
const QString & getFileName() const
File name (corresponding data for simulator, only available if representing simulator model.
Value object for interpolator and rendering per callsign.
const CInterpolationStatus & getInterpolationStatus() const
Get status.
const CPartsStatus & getPartsStatus() const
Get status.
bool hasValidSituation() const
Is the corresponding position valid?
bool updateOwnParts(const aviation::CAircraftParts &parts)
Update own parts.
bool updateCockpit(const swift::misc::simulation::CSimulatedAircraft &aircraft, const swift::misc::CIdentifier &originator)
Update cockpit, but only send signals when applicable.
CSimulatedAircraft getOwnAircraft() const
Own aircraft.
bool isSupportingParts() const
Supporting parts.
Definition: partsstatus.h:26
aviation::CCallsignSet getAircraftInRangeCallsigns() const
Unique callsigns for 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.
bool updateAircraftRendered(const aviation::CCallsign &callsign, bool rendered)
Set aircraft rendered.
Comprehensive information of an aircraft.
bool hasModelString() const
Has model string?
aviation::CTransponder::TransponderMode getTransponderMode() const
Get transponder mode.
const aviation::CAircraftSituation & getSituation() const
Get situation.
const aviation::CComSystem & getCom2System() const
Get COM2 system.
qint32 getTransponderCode() const
Get transponder code.
bool hasCallsign() const
Callsign not empty, no further checks.
const aviation::CCallsign & getCallsign() const
Get callsign.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.
bool hasChangedCockpitData(const aviation::CComSystem &com1, const aviation::CComSystem &com2, const aviation::CTransponder &transponder) const
Changed cockpit data?
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
QString getCallsignAsString() const
Get callsign.
const aviation::CComSystem & getCom1System() const
Get COM1 system.
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code if any.
const aviation::CAircraftParts & getParts() const
Get aircraft parts.
Q_REQUIRED_RESULT CSimulatedAircraftList findByRendered(bool rendered) const
Rendered / not rendered aircraft.
aviation::CCallsignSet getLogCallsigns() const
All callsigns marked to be logged.
Direct threadsafe in memory access to own aircraft.
Direct thread safe in memory access to remote aircraft.
void setSimulatorDetails(const QString &name, const QString &details, const QString &version)
Set version and simulator details from running simulator.
void setDefaultModel(const CAircraftModel &defaultModel)
Default model.
Proxy object connected to a real FGSwiftBus::CService object via DBus.
void getOwnAircraftSituationData(FlightgearData *o_flightgearData)
Get own aircraft situation data.
QString getAircraftModelFilename() const
Get base filename of current aircraft model.
void getGroundElevationAsync(double *o_groundElevation)
Get ground elevation [m] for current airplane position.
void cancelAllPendingAsyncCalls()
Cancel all current async slot calls.
void getAircraftModelPathAsync(QString *o_modelPath)
Get full path to current aircraft model.
void setCom2StandbyKhz(int freq)
Set the current COM2 standby frequency in kHz.
QString getAircraftModelString() const
Get canonical swift model string of current aircraft model.
QString getAircraftName() const
Get name of current aircraft model.
QString getAircraftIcaoCode() const
Get the ICAO code of the current aircraft model.
void getCom2StandbyKhzAsync(int *o_com2Standby)
Get the current COM2 standby frequency in kHz.
void getCom2VolumeAsync(double *o_com2Volume)
Get Com2 volume [0..1].
void setCom1StandbyKhz(int freq)
Set the current COM1 standby frequency in kHz.
void getStrobeLightsOnAsync(bool *o_strobeLightsOn)
Get whether strobe lights are on.
void getCom1VolumeAsync(double *o_com1Volume)
Get Com1 volume [0..1].
QString getAircraftLivery() const
Get current aircraft livery.
void getLandingLightsOnAsync(bool *o_landingLightsOn)
Get whether landing lights are on.
void getTaxiLightsOnAsync(bool *o_taxiLightsOn)
Get whether taxi lights are on.
void getTransponderModeAsync(int *o_xpdrMode)
Get the current transponder mode (depends on the aircraft, 0 and 1 usually mean standby,...
void getNavLightsOnAsync(bool *o_navLightsOn)
Get whether nav lights are on.
void getAllWheelsOnGroundAsync(bool *o_allWheels)
Get whether all wheels are on the ground.
void getCom2ActiveKhzAsync(int *o_com2Active)
Get the current COM2 active frequency in kHz.
QString getAircraftDescription() const
Get the description of the current aircraft model.
void getCom1ActiveKhzAsync(int *o_com1Active)
Get the current COM1 active frequency in kHz.
void setCom2ActiveKhz(int freq)
Set the current COM2 active frequency in kHz.
void isPausedAsync(bool *o_paused)
True if sim is paused.
void getAircraftIcaoCodeAsync(QString *o_icaoCode)
Get the ICAO code of the current aircraft model.
void getTransponderIdentAsync(bool *o_ident)
Get whether we are currently squawking ident.
void setTransponderCode(int code)
Set the current transponder code in decimal.
void getGearDeployRatioAsync(double *o_gearDeployRatio)
Get gear deploy ratio, where 0 is up and 1 is down.
void getSpeedBrakeRatioAsync(double *o_speedBrakeRatio)
Get the ratio how much the speedbrakes surfaces are extended (0.0 is fully retracted,...
void aircraftModelChanged(const QString &path, const QString &filename, const QString &livery, const QString &icao, const QString &modelString, const QString &name, const QString &description)
Own aircraft model changed.
void getBeaconLightsOnAsync(bool *o_beaconLightsOn)
Get whether beacon lights are on.
void getFlapsDeployRatioAsync(double *o_flapsDeployRatio)
Get flaps deploy ratio, where 0.0 is flaps fully retracted, and 1.0 is flaps fully extended.
void getTransponderCodeAsync(int *o_xpdrCode)
Get the current transponder code in decimal.
void getEngineN1PercentageAsync(QList< double > *o_engineN1Percentage)
Get the N1 speed as percent of max (per engine)
void getCom1StandbyKhzAsync(int *o_com1Standby)
Get the current COM1 standby frequency in kHz.
void setTransponderMode(int mode)
Set the current transponder mode (depends on the aircraft, 0 and 1 usually mean standby,...
QString getAircraftModelPath() const
Get full path to current aircraft model.
void setCom1ActiveKhz(int freq)
Set the current COM1 active frequency in kHz.
bool isValid() const
Does the remote object exist?
void getOwnAircraftVelocityData(FlightgearData *o_flightgearData)
Get own aircraft velocity data.
void addTextMessage(const QString &text)
Add a text message to the on-screen display.
Proxy object connected to a real FGSwiftBus::CTraffic object via DBus.
void remoteAircraftAdded(const QString &callsign)
Remote aircraft successfully added.
void setPlanesTransponders(const swift::simplugin::flightgear::PlanesTransponders &planesTransponders)
Set the transponders of multiple traffic aircrafts.
void setPlanesSurfaces(const swift::simplugin::flightgear::PlanesSurfaces &planesSurfaces)
Set the surfaces of multiple traffic aircrafts.
void cleanup()
Reverse the actions of initialize().
void remoteAircraftAddingFailed(const QString &callsign)
Remote aircraft adding failed.
void addPlane(const QString &callsign, const QString &modelName, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery)
Introduce a new traffic aircraft.
bool isValid() const
Does the remote object exist?
void getRemoteAircraftData(const QStringList &callsigns, const RemoteAircraftDataCallback &setter) const
Get remote aircrafts data (lat, lon, elevation and CG)
void removePlane(const QString &callsign)
Remove a traffic aircraft.
void setPlanesPositions(const swift::simplugin::flightgear::PlanesPositions &planesPositions)
Set the position of multiple traffic aircrafts.
Class representing a Flightgear multiplayer aircraft.
const swift::misc::simulation::CSimulatedAircraft & getAircraft() const
Simulated aircraft (as added)
virtual swift::core::ISimulator * create(const swift::misc::simulation::CSimulatorPluginInfo &info, swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider, swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, swift::misc::network::IClientProvider *clientProvider)
Create a new instance of a driver.
virtual swift::misc::CStatusMessageList getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const
Interpolation messages for callsign.
virtual bool updateOwnSimulatorSelcal(const swift::misc::aviation::CSelcal &selcal, const swift::misc::CIdentifier &originator)
Update own aircraft cockpit (usually from context)
virtual void displayTextMessage(const swift::misc::network::CTextMessage &message) const
Display a text message.
virtual bool physicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft)
Add new remote aircraft physically to the simulator.
virtual bool updateOwnSimulatorCockpit(const swift::misc::simulation::CSimulatedAircraft &aircraft, const swift::misc::CIdentifier &originator)
Update own aircraft cockpit (usually from context)
virtual int physicallyRemoveAllRemoteAircraft()
Remove all remote aircraft and their data via ISimulator::clearAllRemoteAircraftData.
virtual bool requestElevation(const swift::misc::geo::ICoordinateGeodetic &reference, const swift::misc::aviation::CCallsign &callsign)
Request elevation, there is no guarantee the requested elevation will be available in the provider.
virtual bool physicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign)
Remove remote aircraft from simulator.
virtual bool isConnected() const
Are we connected to the simulator?
CSimulatorFlightgear(const swift::misc::simulation::CSimulatorPluginInfo &info, swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider, swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, swift::misc::network::IClientProvider *clientProvider, QObject *parent=nullptr)
Constructor.
virtual swift::misc::aviation::CCallsignSet physicallyRenderedAircraft() const
Physically rendered (displayed in simulator) This shall only return aircraft handled in the simulator...
virtual void resetAircraftStatistics()
Reset the statistics.
virtual void displayStatusMessage(const swift::misc::CStatusMessage &message) const
Display a status message in the simulator.
virtual QString getStatisticsSimulatorSpecific() const
Allows to print out simulator specific statistics.
virtual bool disconnectFrom()
Disconnect from simulator.
virtual bool isPhysicallyRenderedAircraft(const swift::misc::aviation::CCallsign &callsign) const
Is the aircraft rendered (displayed in simulator)? This shall only return true if the aircraft is rea...
virtual void clearAllRemoteAircraftData()
Clear all aircraft related data, but do not physically remove the aircraft.
virtual void unload()
Driver will be unloaded.
virtual bool testSendSituationAndParts(const swift::misc::aviation::CCallsign &callsign, const swift::misc::aviation::CAircraftSituation &situation, const swift::misc::aviation::CAircraftParts &parts)
Send situation/parts for testing.
virtual void stopImpl()
Plugin specific implementation to stop listener.
CSimulatorFlightgearListener(const swift::misc::simulation::CSimulatorPluginInfo &info)
Constructor.
virtual void startImpl()
Plugin specific implementation to start listener.
virtual void checkImpl()
Plugin specific implementation to check.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Free functions in swift::misc.
qint64 currentMSecsSinceEpoch()
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection connectToPeer(const QString &address, const QString &name)
void disconnectFromBus(const QString &name)
void disconnectFromPeer(const QString &name)
bool isConnected() const const
QString name() const const
QDBusConnection sessionBus()
void addWatchedService(const QString &newService)
void serviceUnregistered(const QString &serviceName)
void setConnection(const QDBusConnection &connection)
void setWatchMode(QDBusServiceWatcher::WatchMode mode)
void clear()
bool contains(const Key &key) const const
QHash< Key, T >::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QList< Key > keys() const const
bool remove(const Key &key)
qsizetype size() const const
T value(const Key &key) const const
QList< T > values() const const
QList< T >::const_reference at(qsizetype i) const const
QList< T >::const_iterator constBegin() const const
QList< T >::const_iterator constEnd() const const
bool isEmpty() const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setObjectName(QAnyStringView name)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString toLower() const const
QString trimmed() const const
QueuedConnection
void setInterval(int msec)
bool isActive() const const
void start()
void stop()
void timeout()
double pressureAltitudeFt
Pressure altitude [inhg].
QList< double > enginesN1Percentage
N1 per engine [%].
double rollRateRadPerSec
Roll angular velocity [rad/s].
double pitchRateRadPerSec
Pitch angular velocity [rad/s].
double gearReployRatio
Gear deployment ratio [%].
int xpdrMode
Transponder mode (off=0,stdby=1-2, >2 on)
double yawRateRadPerSec
Yaw angular velocity [rad/s].
double flapsReployRatio
Flaps deployment ratio [%].
void push_back(const swift::misc::aviation::CAircraftSituation &situation)
Push back the latest situation.
void push_back(const swift::misc::aviation::CCallsign &callsign, const swift::misc::aviation::CAircraftParts &parts)
Push back the latest parts.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26