refs #395, improvements for snapshot / restricted aircraft handling

* fixed isCurrentThreadCreatingThread and renamed to isCurrentThreadObjectThread (this check was never working), added 2 more thread checks
* changed remove aircraft function to return number of returned aircraft (like with the containers this allows to avoid unnecessary signals)
* removed unused function ps_recalculateRenderedAircraft() / SimulatorCommon
* using Queued airspaceAircraftSnapshot signal for binding (functor connect does not provide connection type)
* extened ASSERTs to check threads
* simulator: initial situation function with return value (success?)
* simulator: avoid unneccessary copy and provide correct rendered flag in add aircraft function
This commit is contained in:
Klaus Basan
2015-05-19 16:13:10 +02:00
parent beef0a5ec9
commit 880a954db9
19 changed files with 194 additions and 118 deletions

View File

@@ -7,7 +7,8 @@
* contained in the LICENSE file.
*/
#include "airspace_analyzer.h"
#include "blackcore/airspace_analyzer.h"
#include "blackcore/blackcorefreefunctions.h"
#include "blackmisc/logmessage.h"
#include <QDateTime>
@@ -30,7 +31,7 @@ namespace BlackCore
// all in new thread from here on
m_timer.setObjectName(this->objectName().append(":m_timer"));
m_timer.start(5000);
m_timer.start(7500);
bool c = connect(&m_timer, &QTimer::timeout, this, &CAirspaceAnalyzer::ps_timeout);
Q_ASSERT(c);
@@ -53,7 +54,8 @@ namespace BlackCore
Q_ASSERT(c);
Q_UNUSED(c);
this->start();
// start in own thread
this->start(QThread::LowestPriority);
}
CAirspaceAircraftSnapshot CAirspaceAnalyzer::getLatestAirspaceAircraftSnapshot() const
@@ -172,6 +174,9 @@ namespace BlackCore
void CAirspaceAnalyzer::analyzeAirspace()
{
Q_ASSERT_X(!isCurrentThreadApplicationThread(), Q_FUNC_INFO, "Expect to run in background thread");
Q_ASSERT_X(!isApplicationThreadObjectThread(this), Q_FUNC_INFO, "Expect to run in background thread affinity");
bool restricted;
int maxAircraft;
CLength maxRenderedDistance, maxRenderedBoundary;
@@ -183,17 +188,24 @@ namespace BlackCore
maxRenderedBoundary = this->m_simulatorMaxRenderedBoundary;
}
//! \todo Analyzer: generate only when restricted?
CSimulatedAircraftList aircraftInRange(getAircraftInRange()); // thread safe copy from provider
CAirspaceAircraftSnapshot snapshot(
getAircraftInRange(), // thread safe copy
aircraftInRange,
restricted, maxAircraft, maxRenderedDistance, maxRenderedBoundary
);
// lock block
{
QWriteLocker l(&m_lockSnapshot);
snapshot.setRestrictionChanged(m_latestAircraftSnapshot);
bool wasValid = m_latestAircraftSnapshot.isValidSnapshot();
m_latestAircraftSnapshot = snapshot;
if (!wasValid) { return; } // ignore the 1st snapshot
snapshot.setRestrictionChanged(m_latestAircraftSnapshot);
}
emit airspaceAircraftSnapshot(snapshot);
}

View File

@@ -58,9 +58,12 @@ namespace BlackCore
this->connect(this->m_vatsimBookingReader, &CVatsimBookingReader::dataRead, this, &CAirspaceMonitor::ps_receivedBookings);
this->connect(this->m_vatsimDataFileReader, &CVatsimDataFileReader::dataRead, this, &CAirspaceMonitor::ps_receivedDataFile);
// Force snapshot in the main event loop
this->connect(this->m_analyzer, &CAirspaceAnalyzer::airspaceAircraftSnapshot, this, &CAirspaceMonitor::airspaceAircraftSnapshot, Qt::QueuedConnection);
// Analyzer
this->connect(this->m_analyzer, &CAirspaceAnalyzer::timeoutAircraft, this, &CAirspaceMonitor::ps_pilotDisconnected);
this->connect(this->m_analyzer, &CAirspaceAnalyzer::timeoutAtc, this, &CAirspaceMonitor::ps_atcControllerDisconnected);
this->connect(this->m_analyzer, &CAirspaceAnalyzer::timeoutAircraft, this, &CAirspaceMonitor::ps_pilotDisconnected, Qt::QueuedConnection);
this->connect(this->m_analyzer, &CAirspaceAnalyzer::timeoutAtc, this, &CAirspaceMonitor::ps_atcControllerDisconnected, Qt::QueuedConnection);
}
CSimulatedAircraftList CAirspaceMonitor::getAircraftInRange() const
@@ -136,14 +139,18 @@ namespace BlackCore
std::function<void(const CAirspaceAircraftSnapshot &)> aircraftSnapshotSlot
)
{
// bind does not allow to define connection type
// so anything in its own thread will be sent with this thread affinity
QMetaObject::Connection c1 = connect(this, &CAirspaceMonitor::addedAircraftSituation, situationSlot);
Q_ASSERT(c1);
Q_ASSERT_X(c1, Q_FUNC_INFO, "connect failed");
QMetaObject::Connection c2 = connect(this, &CAirspaceMonitor::addedAircraftParts, partsSlot);
Q_ASSERT(c2);
Q_ASSERT_X(c2, Q_FUNC_INFO, "connect failed");
QMetaObject::Connection c3 = connect(this, &CAirspaceMonitor::removedAircraft, removedAircraftSlot);
Q_ASSERT(c3);
QMetaObject::Connection c4 = this->connect(this->m_analyzer, &CAirspaceAnalyzer::airspaceAircraftSnapshot, aircraftSnapshotSlot);
Q_ASSERT(c4);
Q_ASSERT_X(c3, Q_FUNC_INFO, "connect failed");
// trick is to use the Queued signal here
// analyzer (own thread) -> airspaceAircraftSnapshot -> AirspaceMonitor -> airspaceAircraftSnapshot queued in main thread
QMetaObject::Connection c4 = this->connect(this, &CAirspaceMonitor::airspaceAircraftSnapshot, aircraftSnapshotSlot);
Q_ASSERT_X(c4, Q_FUNC_INFO, "connect failed");
return QList<QMetaObject::Connection>({ c1, c2, c3, c4});
}
@@ -600,7 +607,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_receivedBookings(const CAtcStationList &bookedStations)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
if (bookedStations.isEmpty())
{
this->m_atcStationsBooked.clear();
@@ -620,7 +627,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_receivedDataFile()
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
for (auto client = this->m_otherClients.begin(); client != this->m_otherClients.end(); ++client)
{
if (client->hasSpecifiedVoiceCapabilities()) { continue; } // we already have voice caps
@@ -667,7 +674,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_atcPositionUpdate(const CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
Q_ASSERT(CComSystem::isValidCivilAviationFrequency(frequency));
if (!this->m_connected) { return; }
CAtcStationList stationsWithCallsign = this->m_atcStationsOnline.findByCallsign(callsign);
@@ -718,7 +725,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_atcControllerDisconnected(const CCallsign &callsign)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
this->m_otherClients.removeByCallsign(callsign);
if (this->m_atcStationsOnline.containsCallsign(callsign))
@@ -735,7 +742,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_atisReceived(const CCallsign &callsign, const CInformationMessage &atisMessage)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
if (!this->m_connected || callsign.isEmpty()) return;
CPropertyIndexVariantMap vm(CAtcStation::IndexAtis, atisMessage.toCVariant());
int changedOnline = this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
@@ -749,7 +756,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_atisVoiceRoomReceived(const CCallsign &callsign, const QString &url)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
if (!this->m_connected) { return; }
QString trimmedUrl = url.trimmed();
CPropertyIndexVariantMap vm({ CAtcStation::IndexVoiceRoom, CVoiceRoom::IndexUrl }, trimmedUrl);
@@ -776,7 +783,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_atisLogoffTimeReceived(const CCallsign &callsign, const QString &zuluTime)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
if (!this->m_connected) { return; }
if (zuluTime.length() == 4)
{
@@ -800,7 +807,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_icaoCodesReceived(const CCallsign &callsign, const CAircraftIcaoData &icaoData)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
Q_ASSERT(!callsign.isEmpty());
if (!this->m_connected) { return; }
@@ -833,7 +840,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_aircraftUpdateReceived(const CAircraftSituation &situation, const CTransponder &transponder)
{
Q_ASSERT_X(BlackCore::isCurrentThreadCreatingThread(this), Q_FUNC_INFO, "Called in different thread");
Q_ASSERT_X(BlackCore::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread");
if (!this->m_connected) { return; }
CCallsign callsign(situation.getCallsign());
@@ -923,7 +930,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_aircraftInterimUpdateReceived(const CAircraftSituation &situation)
{
Q_ASSERT_X(BlackCore::isCurrentThreadCreatingThread(this), Q_FUNC_INFO, "Called in different thread");
Q_ASSERT_X(BlackCore::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Called in different thread");
if (!this->m_connected) { return; }
CCallsign callsign(situation.getCallsign());
@@ -955,14 +962,16 @@ namespace BlackCore
vm.addValue(CAircraft::IndexDistanceToOwnAircraft, distance);
// here I expect always a changed value
{
QWriteLocker l(&m_lockAircraft);
this->m_aircraftInRange.applyIfCallsign(callsign, vm);
}
emit this->changedAircraftInRange();
QWriteLocker l(&m_lockAircraft);
this->m_aircraftInRange.applyIfCallsign(callsign, vm);
}
void CAirspaceMonitor::ps_pilotDisconnected(const CCallsign &callsign)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
// in case of inconsistencies I always remove here
this->m_otherClients.removeByCallsign(callsign);
@@ -989,7 +998,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_frequencyReceived(const CCallsign &callsign, const CFrequency &frequency)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
// update
int changed;
@@ -1003,7 +1012,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_aircraftConfigReceived(const BlackMisc::Aviation::CCallsign &callsign, const QJsonObject &jsonObject, bool isFull)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
CSimulatedAircraft simAircraft(getAircraftInRangeForCallsign(callsign));
@@ -1039,7 +1048,7 @@ namespace BlackCore
void CAirspaceMonitor::ps_sendInterimPositions()
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
Q_ASSERT(BlackCore::isCurrentThreadObjectThread(this));
if (!this->m_connected || !m_sendInterimPositions) { return; }
CSimulatedAircraftList aircrafts = m_aircraftInRange.findBy(&CSimulatedAircraft::fastPositionUpdates, true);
m_network->sendInterimPositions(aircrafts.getCallsigns());

View File

@@ -197,6 +197,9 @@ namespace BlackCore
//! An aircraft disappeared
void removedAircraft(const BlackMisc::Aviation::CCallsign &callsign);
//! \copydoc CAirspaceAnalyzer::airspaceAircraftSnapshot
void airspaceAircraftSnapshot(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot);
private:
BlackMisc::Aviation::CAtcStationList m_atcStationsOnline;
BlackMisc::Aviation::CAtcStationList m_atcStationsBooked;

View File

@@ -17,11 +17,25 @@ namespace BlackCore
qRegisterMetaType<BlackCore::INetwork::ConnectionStatus>();
}
bool isCurrentThreadCreatingThread(QObject *toBeTested)
bool isCurrentThreadObjectThread(QObject *toBeTested)
{
Q_ASSERT_X(toBeTested, Q_FUNC_INFO, "missing QObject");
if (!toBeTested) { return false; }
if (!toBeTested->thread()) { return false; }
return (QThread::currentThreadId() == toBeTested->thread()->currentThreadId());
return (QThread::currentThread() == toBeTested->thread());
}
bool isApplicationThreadObjectThread(QObject *toBeTested)
{
Q_ASSERT_X(toBeTested, Q_FUNC_INFO, "missing QObject");
if (!toBeTested) { return false; }
if (!toBeTested->thread()) { return false; }
return (QCoreApplication::instance()->thread() == toBeTested->thread());
}
bool isCurrentThreadApplicationThread()
{
return (QCoreApplication::instance()->thread() == QThread::currentThread());
}
} // namespace

View File

@@ -14,9 +14,17 @@ namespace BlackCore
//! Register all relevant metadata in BlackCore
BLACKCORE_EXPORT void registerMetadata();
//! Is the current thread the one created the object
//! Is the current thread the QObject's thread?
//! \remarks can be used as ASSERT check for threaded objects
BLACKCORE_EXPORT bool isCurrentThreadCreatingThread(QObject *toBeTested);
BLACKCORE_EXPORT bool isCurrentThreadObjectThread(QObject *toBeTested);
//! Is the application thread the QObject's thread?
//! \remarks can be used as ASSERT check for threaded objects
BLACKCORE_EXPORT bool isApplicationThreadObjectThread(QObject *toBeTested);
//! Is the current thread the Application thread?
//! \remarks can be used as ASSERT check for threaded objects
BLACKCORE_EXPORT bool isCurrentThreadApplicationThread();
} // BlackCore

View File

@@ -214,10 +214,10 @@ namespace BlackCore
virtual bool physicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) = 0;
//! Remove remote aircraft from simulator
virtual bool physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) = 0;
virtual int physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) = 0;
//! Remove all remote aircraft
virtual void physicallyRemoveAllRemoteAircraft() = 0;
virtual int physicallyRemoveAllRemoteAircraft() = 0;
//! Emit the combined status
//! \sa simulatorStatusChanged;

View File

@@ -38,7 +38,7 @@ namespace BlackCore
std::bind(&CSimulatorCommon::ps_remoteProviderAddAircraftSituation, this, std::placeholders::_1),
std::bind(&CSimulatorCommon::ps_remoteProviderAddAircraftParts, this, std::placeholders::_1),
std::bind(&CSimulatorCommon::ps_remoteProviderRemovedAircraft, this, std::placeholders::_1),
std::bind(static_cast<void(CSimulatorCommon::*)(const CAirspaceAircraftSnapshot &)>(&CSimulatorCommon::ps_recalculateRenderedAircraft), this, std::placeholders::_1));
std::bind(&CSimulatorCommon::ps_recalculateRenderedAircraft, this, std::placeholders::_1));
// timer
this->m_oneSecondTimer.setObjectName(this->objectName().append(":m_oneSecondTimer"));
@@ -97,7 +97,7 @@ namespace BlackCore
for (const CSimulatedAircraft &aircraft : m_highlightedAircraft)
{
// get the current state for this aircraft
// it might has been removed in the mean time
// it might has been removed in the meantime
const CCallsign cs(aircraft.getCallsign());
resetAircraftFromBacked(cs);
}
@@ -124,19 +124,26 @@ namespace BlackCore
}
}
void CSimulatorCommon::setInitialAircraftSituationAndParts(CSimulatedAircraft &aircraft) const
bool CSimulatorCommon::setInitialAircraftSituation(CSimulatedAircraft &aircraft) const
{
if (!this->m_interpolator) { return; }
if (!this->m_interpolator) { return false; }
const CCallsign callsign(aircraft.getCallsign());
if (this->remoteAircraftSituationsCount(callsign) < 1) { return; }
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign");
// with an interpolator the interpolated situation is used
// to avoid position jittering
// to avoid position jittering when displayed
qint64 time = QDateTime::currentMSecsSinceEpoch();
IInterpolator::InterpolationStatus interpolationStatus;
CAircraftSituation as(m_interpolator->getInterpolatedSituation(callsign, time, aircraft.isVtol(), interpolationStatus));
if (interpolationStatus.interpolationSucceeded) { aircraft.setSituation(as); }
if (interpolationStatus.interpolationSucceeded)
{
aircraft.setSituation(as);
return true;
}
else
{
return false;
}
}
int CSimulatorCommon::getMaxRenderedAircraft() const
@@ -268,14 +275,14 @@ namespace BlackCore
emit renderRestrictionsChanged(false, getMaxRenderedAircraft(), getMaxRenderedDistance(), getRenderedDistanceBoundary());
}
bool CSimulatorCommon::physicallyRemoveMultipleRemoteAircraft(const CCallsignSet &callsigns)
int CSimulatorCommon::physicallyRemoveMultipleRemoteAircraft(const CCallsignSet &callsigns)
{
int removed = 0;
for (const CCallsign &callsign : callsigns)
{
if (physicallyRemoveRemoteAircraft(callsign)) { removed++; }
}
return removed > 0;
return removed;
}
void CSimulatorCommon::ps_oneSecondTimer()
@@ -283,28 +290,29 @@ namespace BlackCore
blinkHighlightedAircraft();
}
void CSimulatorCommon::ps_recalculateRenderedAircraft()
{
this->ps_recalculateRenderedAircraft(getLatestAirspaceAircraftSnapshot());
}
void CSimulatorCommon::ps_recalculateRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
if (!snapshot.isValidSnapshot()) { return;}
// for unrestricted values all add/remove actions are directly linked
// when changing back from restricted->unrestricted an one time update is required
if (!snapshot.isRestricted() && !snapshot.isRestrictionChanged()) { return; }
Q_ASSERT_X(BlackCore::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Needs to run in object thread");
Q_ASSERT_X(snapshot.generatingThreadName() != QThread::currentThread(), Q_FUNC_INFO, "Expect snapshot from background thread");
// restricted snapshot values?
bool changed = false;
if (snapshot.isRenderingEnabled())
{
CCallsignSet callsignsInSimulator(physicallyRenderedAircraft());
CCallsignSet callsignsInSimulator(physicallyRenderedAircraft()); // state in simulator
CCallsignSet callsignsToBeRemoved(callsignsInSimulator.difference(snapshot.getEnabledAircraftCallsignsByDistance()));
CCallsignSet callsignsToBeAdded(snapshot.getEnabledAircraftCallsignsByDistance().difference(callsignsInSimulator));
this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved);
if (!callsignsToBeRemoved.isEmpty())
{
int r = this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved);
changed = r > 0;
}
if (!callsignsToBeAdded.isEmpty())
{
@@ -312,18 +320,23 @@ namespace BlackCore
for (const CSimulatedAircraft &aircraft : aircraftToBeAdded)
{
Q_ASSERT_X(aircraft.isEnabled(), Q_FUNC_INFO, "Disabled aircraft detected as to be added");
this->physicallyAddRemoteAircraft(aircraft);
bool a = this->physicallyAddRemoteAircraft(aircraft);
changed = changed || a;
}
}
}
else
{
this->physicallyRemoveAllRemoteAircraft();
// no rendering at all, we remove everything
int r = this->physicallyRemoveAllRemoteAircraft();
changed = r > 0;
}
// we handled snapshot
emit airspaceSnapshotHandled();
if (changed)
{
emit airspaceSnapshotHandled();
}
}
void CSimulatorCommon::ps_remoteProviderAddAircraftSituation(const CAircraftSituation &situation)

View File

@@ -93,15 +93,12 @@ namespace BlackCore
virtual void deleteAllRenderingRestrictions() override;
//! \copydoc IContextSimulator::physicallyRemoveRemoteAircraft
virtual bool physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) override;
virtual int physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) override;
protected slots:
//! Slow timer used to highlight aircraft, can be used for other things too
virtual void ps_oneSecondTimer();
//! Recalculate the rendered aircraft
virtual void ps_recalculateRenderedAircraft();
//! Recalculate the rendered aircraft
virtual void ps_recalculateRenderedAircraft(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot);
@@ -130,11 +127,11 @@ namespace BlackCore
//! Blink the highlighted aircraft
void blinkHighlightedAircraft();
//! Restore aircraft from backedn data
//! Restore aircraft from backend data
void resetAircraftFromBacked(const BlackMisc::Aviation::CCallsign &callsign);
//! Override parts and situation from current interpolator values, if any!
void setInitialAircraftSituationAndParts(BlackMisc::Simulation::CSimulatedAircraft &aircraft) const;
//! Override situation from current interpolator values, if any!
bool setInitialAircraftSituation(BlackMisc::Simulation::CSimulatedAircraft &aircraft) const;
bool m_debugMessages = false; //!< Display debug messages
bool m_blinkCycle = false; //!< use for highlighting