11 #include <QWriteLocker>
23 using namespace swift::misc::aviation;
24 using namespace swift::misc::geo;
25 using namespace swift::misc::network;
26 using namespace swift::misc::physical_quantities;
27 using namespace swift::misc::simulation;
28 using namespace swift::core::fsd;
37 Q_ASSERT_X(fsdClient, Q_FUNC_INFO,
"Network object required to connect");
40 this->setObjectName(this->
getName());
42 m_lastWatchdogCallMsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
43 bool c = connect(&
m_updateTimer, &QTimer::timeout,
this, &CAirspaceAnalyzer::onTimeout);
54 c = connect(fsdClient, &CFSDClient::connectionStatusChanged,
this,
55 &CAirspaceAnalyzer::onConnectionStatusChanged, Qt::QueuedConnection);
59 c = connect(fsdClient, &CFSDClient::pilotDataUpdateReceived,
this, &CAirspaceAnalyzer::onNetworkPositionUpdate,
60 Qt::QueuedConnection);
62 c = connect(fsdClient, &CFSDClient::atcDataUpdateReceived,
this, &CAirspaceAnalyzer::watchdogTouchAtcCallsign,
63 Qt::QueuedConnection);
68 &CAirspaceAnalyzer::watchdogTouchAircraftCallsign);
71 &CAirspaceAnalyzer::watchdogRemoveAircraftCallsign);
74 &CAirspaceAnalyzer::onAtcStationDisconnected);
81 this->
start(QThread::LowestPriority);
86 QReadLocker l(&m_lockSnapshot);
87 return m_latestAircraftSnapshot;
91 const CLength &maxRenderedDistance)
93 QWriteLocker l(&m_lockRestrictions);
94 m_simulatorRenderedAircraftRestricted = restricted;
95 m_simulatorRenderingEnabled = enabled;
96 m_simulatorMaxRenderedAircraft = maxAircraft;
97 m_simulatorMaxRenderedDistance = maxRenderedDistance;
105 Q_UNUSED(transponder)
106 this->watchdogTouchAircraftCallsign(situation);
109 void CAirspaceAnalyzer::onAtcStationDisconnected(
const CAtcStation &station)
112 this->watchdogRemoveAtcCallsign(cs);
115 void CAirspaceAnalyzer::watchdogTouchAircraftCallsign(
const CAircraftSituation &situation)
118 Q_ASSERT_X(!cs.
isEmpty(), Q_FUNC_INFO,
"No callsign in situaton");
119 m_aircraftCallsignTimestamps[cs] = QDateTime::currentMSecsSinceEpoch();
122 void CAirspaceAnalyzer::watchdogTouchAtcCallsign(
const CCallsign &callsign,
const CFrequency &frequency,
128 m_atcCallsignTimestamps[callsign] = QDateTime::currentMSecsSinceEpoch();
142 void CAirspaceAnalyzer::onTimeout()
145 this->analyzeAirspace();
146 this->watchdogCheckTimeouts();
151 m_aircraftCallsignTimestamps.clear();
152 m_atcCallsignTimestamps.clear();
154 QWriteLocker l(&m_lockSnapshot);
158 void CAirspaceAnalyzer::watchdogRemoveAircraftCallsign(
const CCallsign &callsign)
160 m_aircraftCallsignTimestamps.remove(callsign);
163 void CAirspaceAnalyzer::watchdogRemoveAtcCallsign(
const CCallsign &callsign)
165 m_atcCallsignTimestamps.remove(callsign);
168 void CAirspaceAnalyzer::watchdogCheckTimeouts()
171 const qint64 currentTimeMsEpoch = QDateTime::currentMSecsSinceEpoch();
172 const qint64 callDiffMs = currentTimeMsEpoch - m_lastWatchdogCallMsSinceEpoch;
173 const qint64 callThresholdMs =
static_cast<qint64
>(
m_updateTimer.interval() * 1.5);
174 m_lastWatchdogCallMsSinceEpoch = currentTimeMsEpoch;
175 if (callDiffMs > callThresholdMs)
178 m_doNotRunAgainBefore = currentTimeMsEpoch + 2 * callThresholdMs;
181 if (m_doNotRunAgainBefore > currentTimeMsEpoch) {
return; }
182 m_doNotRunAgainBefore = -1;
185 const std::chrono::milliseconds aircraftTimeoutMs = m_timeoutAircraft;
186 const std::chrono::milliseconds atcTimeoutMs = m_timeoutAtc;
187 const qint64 timeoutAircraftEpochMs = currentTimeMsEpoch - aircraftTimeoutMs.count();
188 const qint64 timeoutAtcEpochMs = currentTimeMsEpoch - atcTimeoutMs.count();
189 const bool enabled = m_enabledWatchdog;
191 const QList<CCallsign> callsignsAircraft = m_aircraftCallsignTimestamps.keys();
192 for (
const CCallsign &callsign : callsignsAircraft)
196 m_aircraftCallsignTimestamps[callsign] = timeoutAircraftEpochMs + 1000;
198 const qint64 tsv = m_aircraftCallsignTimestamps.value(callsign);
199 if (tsv > timeoutAircraftEpochMs) {
continue; }
200 CLogMessage(
this).
debug() << QStringLiteral(
"Aircraft '%1' timed out after %2ms")
202 .arg(currentTimeMsEpoch - tsv);
203 m_aircraftCallsignTimestamps.remove(callsign);
207 const QList<CCallsign> callsignsAtc = m_atcCallsignTimestamps.keys();
208 for (
const CCallsign &callsign : callsignsAtc)
212 m_aircraftCallsignTimestamps[callsign] = timeoutAtcEpochMs + 1000;
214 const qint64 tsv = m_atcCallsignTimestamps.value(callsign);
215 if (tsv > timeoutAtcEpochMs) {
continue; }
216 CLogMessage(
this).
debug() << QStringLiteral(
"ATC '%1' timed out after %2ms")
218 .arg(currentTimeMsEpoch - tsv);
219 m_atcCallsignTimestamps.remove(callsign);
224 void CAirspaceAnalyzer::analyzeAirspace()
227 Q_ASSERT_X(thread() != qApp->thread(), Q_FUNC_INFO,
"Expect to run in background thread affinity");
229 bool restricted, enabled;
233 QReadLocker l(&m_lockRestrictions);
234 restricted = m_simulatorRenderedAircraftRestricted;
235 enabled = m_simulatorRenderingEnabled;
236 maxAircraft = m_simulatorMaxRenderedAircraft;
237 maxRenderedDistance = m_simulatorMaxRenderedDistance;
248 QWriteLocker l(&m_lockSnapshot);
250 if (wasValid) { snapshot.setRestrictionChanged(m_latestAircraftSnapshot); }
251 m_latestAircraftSnapshot = snapshot;
252 if (!wasValid) {
return; }
void timeoutAircraft(const swift::misc::aviation::CCallsign &callsign)
Callsign has timed out.
virtual ~CAirspaceAnalyzer()
Destructor.
void setSimulatorRenderRestrictionsChanged(bool restricted, bool enabled, int maxAircraft, const swift::misc::physical_quantities::CLength &maxRenderedDistance)
Render restrictions in simulator.
void timeoutAtc(const swift::misc::aviation::CCallsign &callsign)
Callsign has timed out.
void airspaceAircraftSnapshot(const swift::misc::simulation::CAirspaceAircraftSnapshot &snapshot)
New aircraft snapshot.
swift::misc::simulation::CAirspaceAircraftSnapshot getLatestAirspaceAircraftSnapshot() const
Get the latest snapshot.
Keeps track of other entities in the airspace: aircraft, ATC stations, etc. Central instance of data ...
void atcStationDisconnected(const swift::misc::aviation::CAtcStation &station)
ATC station disconnected.
FSD client Todo: Send (interim) data updates automatically Todo Check ':' in FSD messages....
Base class for a long-lived worker object which lives in its own thread.
bool isEnabled() const
Enabled (running)?
const QString & getName()
Name of the worker.
QTimer m_updateTimer
timer which can be used by implementing classes
void start(QThread::Priority priority=QThread::InheritPriority)
Starts a thread and moves the worker into it.
Class for emitting a log message.
Derived & debug()
Set the severity to debug.
static bool thisIsMainThread()
Is the current thread the application thread?
Value object encapsulating information of an aircraft's situation.
const CCallsign & getCallsign() const
Corresponding callsign.
Value object encapsulating information about an ATC station.
const CCallsign & getCallsign() const
Get callsign.
Value object encapsulating information of a callsign.
bool isEmpty() const
Is empty?
QString toQString(bool i18n=false) const
Cast as QString.
Value object encapsulating information about a connection status.
bool isConnected() const
Query status.
bool isDisconnected() const
Query status.
Physical unit length (length)
Current situation in the skies analyzed.
bool isValidSnapshot() const
Valid snapshot?
Delegating class which can be directly used to access an.
Class which can be directly used to access an.
CSimulatedAircraftList getAircraftInRange() const
All remote aircraft.
void addedAircraftSituation(const swift::misc::aviation::CAircraftSituation &situation)
Situation added.
void removedAircraft(const swift::misc::aviation::CCallsign &callsign)
An aircraft disappeared.
Value object encapsulating a list of aircraft.
Direct threadsafe in memory access to own aircraft.
Backend services of the swift project, like dealing with the network or the simulators.
Free functions in swift::misc.