refs #395, moved thread safe aircraft parts / situations (from interpolator) to airspace

* access via provider, access now possible also beyond the scope of interpolator
* will be used in (to be written airpace analyzer)
* renamed some member functions as required
* fixed some asserts / Doxygen comments
* adjusted unit tests
* added vtolAircraft flag for interpolator, not fully used everywhere

remarks: update signals for parts / situation still only needed in XP driver
This commit is contained in:
Klaus Basan
2015-04-21 04:01:09 +02:00
committed by Mathew Sutcliffe
parent 32e65669a3
commit 48188dd28d
17 changed files with 356 additions and 506 deletions

View File

@@ -77,53 +77,53 @@ namespace BlackCore
return m_aircraftInRange;
}
const CAircraftSituationList &CAirspaceMonitor::remoteAircraftSituations() const
CAircraftSituationList CAirspaceMonitor::remoteAircraftSituations(const CCallsign &callsign) const
{
// not thread safe, check
Q_ASSERT(this->thread() == QThread::currentThread());
return m_aircraftSituations;
QReadLocker l(&m_lockSituations);
static const CAircraftSituationList empty;
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
return m_situationsByCallsign[callsign];
}
CAircraftSituationList &CAirspaceMonitor::remoteAircraftSituations()
int CAirspaceMonitor::remoteAircraftSituationsCount(const CCallsign &callsign) const
{
// not thread safe, check
Q_ASSERT(this->thread() == QThread::currentThread());
return m_aircraftSituations;
QReadLocker l(&m_lockSituations);
if (!m_situationsByCallsign.contains(callsign)) { return -1; }
return m_situationsByCallsign[callsign].size();
}
CAircraftSituationList CAirspaceMonitor::getRenderedAircraftSituations() const
CAircraftPartsList CAirspaceMonitor::remoteAircraftParts(const CCallsign &callsign, qint64 cutoffTimeValuesBefore) const
{
Q_ASSERT(this->thread() == QThread::currentThread());
return this->m_aircraftSituations;
QReadLocker l(&m_lockParts);
static const CAircraftPartsList empty;
if (!m_partsByCallsign.contains(callsign)) { return empty; }
if (cutoffTimeValuesBefore < 0)
{
return m_partsByCallsign[callsign];
}
else
{
return m_partsByCallsign[callsign].findBefore(cutoffTimeValuesBefore);
}
}
const CAircraftPartsList &CAirspaceMonitor::remoteAircraftParts() const
bool CAirspaceMonitor::isRemoteAircraftSupportingParts(const CCallsign &callsign) const
{
// not thread safe, check
Q_ASSERT(this->thread() == QThread::currentThread());
return m_aircraftParts;
QReadLocker l(&m_lockParts);
return m_aircraftSupportingParts.contains(callsign);
}
CAircraftPartsList &CAirspaceMonitor::remoteAircraftParts()
CCallsignSet CAirspaceMonitor::remoteAircraftSupportingParts() const
{
// not thread safe, check
Q_ASSERT(this->thread() == QThread::currentThread());
return m_aircraftParts;
}
CAircraftPartsList CAirspaceMonitor::getRenderedAircraftParts() const
{
Q_ASSERT(this->thread() == QThread::currentThread());
return this->m_aircraftParts;
QReadLocker l(&m_lockParts);
return m_aircraftSupportingParts;
}
bool CAirspaceMonitor::connectRemoteAircraftProviderSignals(
std::function<void(const CAircraftSituation &)> situationSlot,
std::function<void(const CAircraftParts &)> partsSlot,
std::function<void(const CCallsign &)> removedAircraftSlot
)
std::function<void(const CCallsign &)> removedAircraftSlot)
{
// bool s1 = connect(this, &CAirspaceMonitor::addedRemoteAircraftSituation, situationSlot);
bool s1 = connect(this->m_network, &INetwork::aircraftPositionUpdate, situationSlot);
Q_ASSERT(s1);
bool s2 = connect(this, &CAirspaceMonitor::addedRemoteAircraftParts, partsSlot);
@@ -516,8 +516,16 @@ namespace BlackCore
const CCallsign cs(aircraft.getCallsign());
emit removedRemoteAircraft(cs);
}
m_aircraftSituations.clear();
m_aircraftParts.clear();
// block for lock scope
{
QWriteLocker l1(&m_lockParts);
QWriteLocker l2(&m_lockSituations);
m_situationsByCallsign.clear();
m_partsByCallsign.clear();
m_aircraftSupportingParts.clear();
}
m_aircraftInRange.clear();
m_flightPlanCache.clear();
m_icaoCodeCache.clear();
@@ -769,8 +777,7 @@ namespace BlackCore
Q_ASSERT_X(!callsign.isEmpty(), "ps_aircraftUpdateReceived", "Empty callsign");
// store situation history
this->m_aircraftSituations.push_front(situation);
this->m_aircraftSituations.removeOlderThanNowMinusOffset(AircraftSituationsRemovedOffsetMs);
this->storeAircraftSituation(situation);
emit this->addedRemoteAircraftSituation(situation);
bool exists = this->m_aircraftInRange.containsCallsign(callsign);
@@ -892,15 +899,22 @@ namespace BlackCore
void CAirspaceMonitor::ps_pilotDisconnected(const CCallsign &callsign)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
bool contains = this->m_aircraftInRange.containsCallsign(callsign);
// if with contains false remove here, in case of inconsistencies
// in case of inconsistencies I always remove here
this->m_aircraftWatchdog.removeCallsign(callsign);
this->m_otherClients.removeByCallsign(callsign);
this->m_aircraftSituations.removeByCallsign(callsign);
this->m_aircraftParts.removeByCallsign(callsign);
this->removeFromAircraftCaches(callsign);
// block for lock scope
{
QWriteLocker l1(&m_lockParts);
QWriteLocker l2(&m_lockSituations);
m_situationsByCallsign.remove(callsign);
m_partsByCallsign.remove(callsign);
m_aircraftSupportingParts.remove(callsign);
}
bool contains = this->m_aircraftInRange.containsCallsign(callsign);
if (contains)
{
this->m_aircraftInRange.removeByCallsign(callsign);
@@ -938,7 +952,7 @@ namespace BlackCore
else
{
// incremental update
parts = m_aircraftParts.findFirstByCallsign(callsign);
parts = this->remoteAircraftParts(callsign).findFirstByCallsign(callsign);
QJsonObject config = applyIncrementalObject(parts.toJson(), jsonObject);
parts.convertFromJson(config);
}
@@ -947,9 +961,8 @@ namespace BlackCore
parts.setCurrentUtcTime();
parts.setCallsign(callsign);
// store part history
this->m_aircraftParts.push_front(parts);
this->m_aircraftParts.removeOlderThanNowMinusOffset(AircraftPartsRemoveOffsetMs);
// store part history (parts always absolute)
this->storeAircraftParts(parts);
emit this->addedRemoteAircraftParts(parts);
// here I expect always a changed value
@@ -963,6 +976,40 @@ namespace BlackCore
if (!this->m_connected || !m_sendInterimPositions) { return; }
CSimulatedAircraftList aircrafts = m_aircraftInRange.findBy(&CSimulatedAircraft::fastPositionUpdates, true);
m_network->sendInterimPosition(aircrafts.getCallsigns());
void CAirspaceMonitor::storeAircraftSituation(const CAircraftSituation &situation)
{
QWriteLocker lock(&m_lockSituations);
const CCallsign callsign(situation.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), "storeAircraftSituation", "empty callsign");
if (callsign.isEmpty()) { return; }
// list from new to old
CAircraftSituationList &l = this->m_situationsByCallsign[callsign];
l.push_frontMaxElements(situation, MaxSituationsPerCallsign);
// check sort order
Q_ASSERT_X(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch(), "storeAircraftSituation", "wrong sort order");
}
void CAirspaceMonitor::storeAircraftParts(const CAircraftParts &parts)
{
QWriteLocker lock(&m_lockParts);
const CCallsign callsign(parts.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), "storeAircraftParts", "empty callsign");
if (callsign.isEmpty()) { return; }
// list sorted from new to old
CAircraftPartsList &l = this->m_partsByCallsign[callsign];
l.push_frontMaxElements(parts, MaxPartsPerCallsign);
if (!m_aircraftSupportingParts.contains(callsign))
{
m_aircraftSupportingParts.push_back(callsign); // mark as callsign which supports parts
}
// check sort order
Q_ASSERT_X(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch(), "storeAircraftParts", "wrong sort order");
}
} // namespace

View File

@@ -43,25 +43,28 @@ namespace BlackCore
//! Constructor
CAirspaceMonitor(QObject *parent, const BlackMisc::Simulation::IOwnAircraftProviderReadOnly *ownAircraft, INetwork *network, CVatsimBookingReader *bookings, CVatsimDataFileReader *dataFile);
//! \copydoc IRemoteAircraftProviderReadOnly::renderedAircraft
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraft
virtual const BlackMisc::Simulation::CSimulatedAircraftList &remoteAircraft() const override;
//! \copydoc IRemoteAircraftProvider::renderedAircraft
//! \copydoc IRemoteAircraftProvider::remoteAircraft
virtual BlackMisc::Simulation::CSimulatedAircraftList &remoteAircraft() override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftSituations
virtual const BlackMisc::Aviation::CAircraftSituationList &remoteAircraftSituations() const override;
virtual BlackMisc::Aviation::CAircraftSituationList remoteAircraftSituations(const BlackMisc::Aviation::CCallsign &callsign) const override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftSituations
virtual BlackMisc::Aviation::CAircraftSituationList &remoteAircraftSituations() override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftSituationsCount
virtual int remoteAircraftSituationsCount(const BlackMisc::Aviation::CCallsign &callsign) const override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftParts
virtual const BlackMisc::Aviation::CAircraftPartsList &remoteAircraftParts() const override;
virtual BlackMisc::Aviation::CAircraftPartsList remoteAircraftParts(const BlackMisc::Aviation::CCallsign &callsign, qint64 cutoffTimeValuesBefore = -1) const override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftParts
virtual BlackMisc::Aviation::CAircraftPartsList &remoteAircraftParts() override;
//! \copydoc IRemoteAircraftProviderReadOnly::isRemoteAircraftSupportingParts
virtual bool isRemoteAircraftSupportingParts(const BlackMisc::Aviation::CCallsign &callsign) const override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftParts
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraftSupportingParts
virtual BlackMisc::Aviation::CCallsignSet remoteAircraftSupportingParts() const override;
//! \copydoc IRemoteAircraftProvider::updateAircraftEnabled
virtual bool updateAircraftEnabled(const BlackMisc::Aviation::CCallsign &callsign, bool enabledForRedering, const QString &originator) override;
//! \copydoc IRemoteAircraftProvider::updateAircraftModel
@@ -116,12 +119,6 @@ namespace BlackCore
//! Test injected aircraft parts
void testAddAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts, bool incremental);
//! Aircraft situations
virtual BlackMisc::Aviation::CAircraftSituationList getRenderedAircraftSituations() const;
//! Aircraft parts
virtual BlackMisc::Aviation::CAircraftPartsList getRenderedAircraftParts() const;
//! \copydoc IRemoteAircraftProviderReadOnly::connectSignals
//! \copydoc IRemoteAircraftProviderReadOnly::connectSignals
virtual bool connectRemoteAircraftProviderSignals(
@@ -143,7 +140,7 @@ namespace BlackCore
//--- signals for the provider, work locally only (not in DBus)
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftSituation
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftPart
void addedRemoteAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftPart
@@ -152,6 +149,8 @@ namespace BlackCore
//! \copydoc IRemoteAircraftProviderReadOnly::removedAircraft
void removedRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign);
//--- DBus / local signals
//! Online ATC stations were changed
void changedAtcStationsOnline();
@@ -175,8 +174,11 @@ namespace BlackCore
BlackMisc::Aviation::CAtcStationList m_atcStationsBooked;
BlackMisc::Network::CClientList m_otherClients;
BlackMisc::Simulation::CSimulatedAircraftList m_aircraftInRange;
BlackMisc::Aviation::CAircraftSituationList m_aircraftSituations;
BlackMisc::Aviation::CAircraftPartsList m_aircraftParts;
// hashs, because not sorted by key but keeping order
CSituationsPerCallsign m_situationsByCallsign; //!< situations, for performance reasons per callsign
CPartsPerCallsign m_partsByCallsign; //!< parts, for performance reasons per callsign
BlackMisc::Aviation::CCallsignSet m_aircraftSupportingParts; //!< aircraft supporting parts
QMap<BlackMisc::Aviation::CAirportIcao, BlackMisc::Aviation::CInformationMessage> m_metarCache;
QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CFlightPlan> m_flightPlanCache;
@@ -190,8 +192,17 @@ namespace BlackCore
bool m_serverSupportsNameQuery = false; //!< not all servers support name query
bool m_connected = false; //!< retrieve data
bool m_sendInterimPositions = false;
QTimer m_interimPositionUpdateTimer;
QTimer m_interimPositionUpdateTimer;
// locks
mutable QReadWriteLock m_lockSituations; //!< lock for situations
mutable QReadWriteLock m_lockParts; //!< lock for parts
// TODO FIXME (MS) should be in INetwork
void sendFsipiCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
void sendFsipirCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
QStringList createFsipiCustomPacketData() const;
//! Remove ATC online stations
void removeAllOnlineAtcStations();
@@ -208,6 +219,14 @@ namespace BlackCore
//! Schedule a ready for model matching
void fireDelayedReadyForModelMatching(const BlackMisc::Aviation::CCallsign &callsign, int trial = 1, int delayMs = 2500);
//! Store an aircraft situation
//! \threadsafe
void storeAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! Store an aircraft part
//! \threadsafe
void storeAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);
private slots:
//! Create aircraft in range, this is the only place where a new aircraft should be added
void ps_aircraftUpdateReceived(const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);

View File

@@ -78,12 +78,13 @@ namespace BlackCore
connect(this->m_airspace, &CAirspaceMonitor::changedAtcStationsBooked, this, &CContextNetwork::changedAtcStationsBooked);
connect(this->m_airspace, &CAirspaceMonitor::changedAtcStationOnlineConnectionStatus, this, &CContextNetwork::changedAtcStationOnlineConnectionStatus);
connect(this->m_airspace, &CAirspaceMonitor::changedAircraftInRange, this, &CContextNetwork::changedAircraftInRange);
connect(this->m_airspace, &CAirspaceMonitor::removedRemoteAircraft, this, &IContextNetwork::removedAircraft);
connect(this->m_airspace, &CAirspaceMonitor::removedRemoteAircraft, this, &CContextNetwork::removedRemoteAircraft);
connect(this->m_airspace, &CAirspaceMonitor::removedRemoteAircraft, this, &IContextNetwork::removedAircraft); // DBus
connect(this->m_airspace, &CAirspaceMonitor::readyForModelMatching, this, &CContextNetwork::readyForModelMatching);
connect(this->m_airspace, &CAirspaceMonitor::addedRemoteAircraftParts, this, &CContextNetwork::addedRemoteAircraftParts);
connect(this->m_airspace, &CAirspaceMonitor::addedRemoteAircraftSituation, this, &CContextNetwork::addedRemoteAircraftSituation);
connect(this->m_airspace, &CAirspaceMonitor::addedAircraft, this, &CContextNetwork::addedAircraft);
// remote provider, local only
connect(this->m_airspace, &CAirspaceMonitor::removedRemoteAircraft, this, &CContextNetwork::removedRemoteAircraft); // Local
connect(this->m_airspace, &CAirspaceMonitor::addedRemoteAircraftParts, this, &CContextNetwork::addedRemoteAircraftParts);
}
CContextNetwork::~CContextNetwork()
@@ -103,28 +104,34 @@ namespace BlackCore
return m_airspace->remoteAircraft();
}
CAircraftSituationList &CContextNetwork::remoteAircraftSituations()
CAircraftSituationList CContextNetwork::remoteAircraftSituations(const CCallsign &callsign) const
{
Q_ASSERT(this->m_airspace);
return m_airspace->remoteAircraftSituations();
return m_airspace->remoteAircraftSituations(callsign);
}
const CAircraftSituationList &CContextNetwork::remoteAircraftSituations() const
CAircraftPartsList CContextNetwork::remoteAircraftParts(const CCallsign &callsign, qint64 cutoffTimeBefore) const
{
Q_ASSERT(this->m_airspace);
return m_airspace->remoteAircraftSituations();
return m_airspace->remoteAircraftParts(callsign, cutoffTimeBefore);
}
const CAircraftPartsList &CContextNetwork::remoteAircraftParts() const
int CContextNetwork::remoteAircraftSituationsCount(const CCallsign &callsign) const
{
Q_ASSERT(this->m_airspace);
return m_airspace->remoteAircraftParts();
return m_airspace->remoteAircraftSituationsCount(callsign);
}
CAircraftPartsList &CContextNetwork::remoteAircraftParts()
bool CContextNetwork::isRemoteAircraftSupportingParts(const CCallsign &callsign) const
{
Q_ASSERT(this->m_airspace);
return m_airspace->remoteAircraftParts();
return m_airspace->isRemoteAircraftSupportingParts(callsign);
}
CCallsignSet CContextNetwork::remoteAircraftSupportingParts() const
{
Q_ASSERT(this->m_airspace);
return m_airspace->remoteAircraftSupportingParts();
}
bool CContextNetwork::connectRemoteAircraftProviderSignals(

View File

@@ -51,23 +51,26 @@ namespace BlackCore
//! Destructor
virtual ~CContextNetwork();
//! \copydoc IRemoteAircraftProviderReadOnly::renderedAircraft
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraft
virtual const BlackMisc::Simulation::CSimulatedAircraftList &remoteAircraft() const override;
//! \copydoc IRenderedAircraftProvider::renderedAircraft
//! \copydoc IRenderedAircraftProvider::remoteAircraft
virtual BlackMisc::Simulation::CSimulatedAircraftList &remoteAircraft() override;
//! \copydoc IRemoteAircraftProviderReadOnly::renderedAircraftSituations
virtual const BlackMisc::Aviation::CAircraftSituationList &remoteAircraftSituations() const override;
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraftSituations
virtual BlackMisc::Aviation::CAircraftSituationList remoteAircraftSituations(const BlackMisc::Aviation::CCallsign &callsign) const override;
//! \copydoc IRemoteAircraftProviderReadOnly::renderedAircraftSituations
virtual BlackMisc::Aviation::CAircraftSituationList &remoteAircraftSituations() override;
//! \copydoc IRemoteAircraftProvider::remoteAircraftSituationsCount
virtual int remoteAircraftSituationsCount(const BlackMisc::Aviation::CCallsign &callsign) const override;
//! \copydoc IRemoteAircraftProviderReadOnly::renderedAircraftParts
virtual const BlackMisc::Aviation::CAircraftPartsList &remoteAircraftParts() const override;
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraftParts
virtual BlackMisc::Aviation::CAircraftPartsList remoteAircraftParts(const BlackMisc::Aviation::CCallsign &callsign, qint64 cutoffTimeBefore = -1) const override;
//! \copydoc IRenderedAircraftProvider::renderedAircraftParts
virtual BlackMisc::Aviation::CAircraftPartsList &remoteAircraftParts() override;
//! \copydoc IRemoteAircraftProviderReadOnly::isRemoteAircraftSupportingParts
virtual bool isRemoteAircraftSupportingParts(const BlackMisc::Aviation::CCallsign &callsign) const;
//! \copydoc IRemoteAircraftProviderReadOnly::remoteAircraftSupportingParts
virtual BlackMisc::Aviation::CCallsignSet remoteAircraftSupportingParts() const override;
//! \copydoc IRemoteAircraftProviderReadOnly::connectSignals
virtual bool connectRemoteAircraftProviderSignals(
@@ -77,9 +80,6 @@ namespace BlackCore
) override;
signals:
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftSituation
void addedRemoteAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftPart
void addedRemoteAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);

View File

@@ -23,101 +23,18 @@ namespace BlackCore
CContinuousWorker(parent, workerName),
CRemoteAircraftAwareReadOnly(provider)
{
Q_ASSERT(provider);
this->m_situationsByCallsign = this->remoteAircraftSituations().splitPerCallsign();
this->m_partsByCallsign = this->remoteAircraftParts().splitPerCallsign();
bool c = provider->connectRemoteAircraftProviderSignals(
std::bind(&IInterpolator::ps_onAddedAircraftSituation, this, std::placeholders::_1),
std::bind(&IInterpolator::ps_onAddedAircraftParts, this, std::placeholders::_1),
std::bind(&IInterpolator::ps_onRemovedAircraft, this, std::placeholders::_1)
);
Q_ASSERT(c);
Q_UNUSED(c);
Q_ASSERT_X(provider, Q_FUNC_INFO, "missing provider");
}
CAircraftSituationList IInterpolator::getInterpolatedSituations(qint64 currentTimeMsSinceEpoch)
{
QReadLocker l(&m_lockSituations);
const CSituationsPerCallsign situationsCopy(m_situationsByCallsign);
l.unlock();
CAircraftSituationList latestInterpolations;
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
for (const CCallsign &cs : situationsCopy.keys())
{
InterpolationStatus status;
CAircraftSituation situation = getInterpolatedSituation(cs, currentTimeMsSinceEpoch, status, &situationsCopy);
if (status.allTrue())
{
latestInterpolations.push_back(situation);
}
else
{
// not OK can mean not enough situations
// further logging could go here
}
}
return latestInterpolations;
}
IInterpolator::CSituationsPerCallsign IInterpolator::getSituationsByCallsign() const
{
QReadLocker l(&m_lockSituations);
return m_situationsByCallsign;
}
CAircraftPartsList IInterpolator::getAndRemovePartsBeforeTime(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus)
CAircraftPartsList IInterpolator::getPartsBeforeTime(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus)
{
static const CAircraftPartsList empty;
Q_ASSERT_X(!callsign.isEmpty(), "getAndRemovePartsBeforeTime", "empty callsign");
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign");
partsStatus.reset();
QWriteLocker l(&m_lockParts);
if (this->m_partsByCallsign.contains(callsign))
{
partsStatus.supportsParts = true;
if (cutoffTime < 0) { return this->m_partsByCallsign[callsign]; }
return this->m_partsByCallsign[callsign].findBeforeAndRemove(cutoffTime);
}
else
{
// did we ever have parts for this callsign
partsStatus.supportsParts = m_aircraftSupportingParts.contains(callsign);
return empty;
}
}
void IInterpolator::clear()
{
QWriteLocker s(&m_lockSituations);
QWriteLocker p(&m_lockParts);
m_situationsByCallsign.clear();
m_partsByCallsign.clear();
}
bool IInterpolator::hasDataForCallsign(const CCallsign &callsign) const
{
if (callsign.isEmpty()) { return false; }
QReadLocker s(&m_lockSituations);
if (m_situationsByCallsign.contains(callsign)) { return true; }
QReadLocker p(&m_lockParts);
return m_partsByCallsign.contains(callsign);
}
CAircraftSituationList IInterpolator::getSituationsForCallsign(const CCallsign &callsign) const
{
QReadLocker l(&m_lockSituations);
static const CAircraftSituationList empty;
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
return m_situationsByCallsign[callsign];
}
CAircraftPartsList IInterpolator::getPartsForCallsign(const CCallsign &callsign) const
{
QReadLocker l(&m_lockParts);
static const CAircraftPartsList empty;
if (!m_partsByCallsign.contains(callsign)) { return empty; }
return m_partsByCallsign[callsign];
partsStatus.supportsParts = this->isRemoteAircraftSupportingParts(callsign);
if (!partsStatus.supportsParts) { return empty; }
return this->remoteAircraftParts(callsign, cutoffTime);
}
void IInterpolator::enableDebugMessages(bool enabled)
@@ -125,61 +42,6 @@ namespace BlackCore
this->m_withDebugMsg = enabled;
}
void IInterpolator::forceSortingOfAddedValues(bool sort)
{
this->m_forceSortWhenAddingValues = sort;
}
void IInterpolator::ps_onAddedAircraftSituation(const CAircraftSituation &situation)
{
QWriteLocker lock(&m_lockSituations);
const CCallsign callsign(situation.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), "ps_onAddedAircraftSituation", "empty callsign");
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << situation.getCallsign() << situation.getMSecsSinceEpoch(); }
// list from new to old
CAircraftSituationList &l = this->m_situationsByCallsign[callsign];
l.push_frontMaxElements(situation, MaxSituationsPerCallsign);
if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); }
// check sort order
Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch());
}
void IInterpolator::ps_onAddedAircraftParts(const CAircraftParts &parts)
{
QWriteLocker lock(&m_lockParts);
const CCallsign callsign(parts.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), "ps_onAddedAircraftParts", "empty callsign");
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << parts.getCallsign() << parts.getMSecsSinceEpoch(); }
// list sorted from new to old
CAircraftPartsList &l = this->m_partsByCallsign[callsign];
l.push_frontMaxElements(parts, MaxPartsPerCallsign);
if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); }
if (m_aircraftSupportingParts.contains(callsign)) { return; }
m_aircraftSupportingParts.push_back(callsign); // mark as callsign which supports parts
// check sort order
Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch());
}
void IInterpolator::ps_onRemovedAircraft(const CCallsign &callsign)
{
QWriteLocker ls(&m_lockSituations);
QWriteLocker lp(&m_lockParts);
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
this->m_partsByCallsign.remove(callsign);
this->m_situationsByCallsign.remove(callsign);
this->m_aircraftSupportingParts.remove(callsign);
}
bool IInterpolator::InterpolationStatus::allTrue() const
{
return interpolationSucceeded && changedPosition;

View File

@@ -29,12 +29,6 @@ namespace BlackCore
Q_OBJECT
public:
//! Situations per callsign
typedef QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> CSituationsPerCallsign;
//! Parts per callsign
typedef QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftPartsList> CPartsPerCallsign;
//! Virtual destructor
virtual ~IInterpolator() {}
@@ -70,79 +64,22 @@ namespace BlackCore
//! Current interpolated situation
//! \threadsafe
virtual BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(
const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc,
InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign = nullptr) const = 0;
//! Do a complete calculation for all know callsigns.
//! \param currentTimeMsSinceEpoch if no value is passed current time is used
//! \threadsafe
//!
virtual BlackMisc::Aviation::CAircraftSituationList getInterpolatedSituations(qint64 currentTimeMsSinceEpoch = -1);
//! All situations for all callsigns (in map as per callsign)
//! \threadsafe
CSituationsPerCallsign getSituationsByCallsign() const;
const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc, bool isVtolAircraft, InterpolationStatus &status) const = 0;
//! Parts before given offset time (aka pending parts)
//! \threadsafe
virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeTime(const BlackMisc::Aviation::CCallsign &callsign, qint64 cutoffTime, PartsStatus &partsStatus);
//! Clear all data
//! \threadsafe
virtual void clear();
//! Does know callsign?
//! \threadsafe
virtual bool hasDataForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! All situations for given callsign
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList getSituationsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! All parts for given callsign
//! \threadsafe
BlackMisc::Aviation::CAircraftPartsList getPartsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
virtual BlackMisc::Aviation::CAircraftPartsList getPartsBeforeTime(const BlackMisc::Aviation::CCallsign &callsign, qint64 cutoffTime, PartsStatus &partsStatus);
//! Enable debug messages
void enableDebugMessages(bool enabled);
//! Force sorting (latest first), not required if order can be guaranteed
void forceSortingOfAddedValues(bool sort);
static const qint64 TimeOffsetMs = 6000; //!< offset for interpolation
static const int MaxSituationsPerCallsign = 6; //!< How many situations per callsign
static const int MaxPartsPerCallsign = 3; //!< How many parts per callsign
static const int MaxKeptInterpolationRequests = 3; //!< How many requests are stored
private slots:
//! New situation got added
//! \threadsafe
void ps_onAddedAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! Added aircraft parts
//! \threadsafe
void ps_onAddedAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);
//! Removed aircraft
//! \threadsafe
void ps_onRemovedAircraft(const BlackMisc::Aviation::CCallsign &callsign);
protected:
//! Constructor
IInterpolator(BlackMisc::Simulation::IRemoteAircraftProviderReadOnly *provider, const QString &workerName, QObject *parent = nullptr);
bool m_withDebugMsg = false; //!< allows to disable debug messages
bool m_forceSortWhenAddingValues = false; //!< force sorting (latest first) when adding values
BlackMisc::Aviation::CCallsignSet m_aircraftSupportingParts; //!< aircraft supporting parts
// hashs, because not sorted by key but keeping order
CSituationsPerCallsign m_situationsByCallsign; //!< situations
CPartsPerCallsign m_partsByCallsign; //!< parts
// locks
mutable QReadWriteLock m_lockSituations; //!< lock for situations
mutable QReadWriteLock m_lockParts; //!< lock for parts
bool m_withDebugMsg = false; //!< allows to disable debug messages
};
} // namespace

View File

@@ -20,30 +20,20 @@ using namespace BlackMisc::Aviation;
namespace BlackCore
{
CAircraftSituation CInterpolatorLinear::getInterpolatedSituation(const CCallsign &callsign, qint64 currentTimeMsSinceEpoc, InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign) const
CAircraftSituation CInterpolatorLinear::getInterpolatedSituation(const CCallsign &callsign, qint64 currentTimeMsSinceEpoc, bool vtolAiracraft, InterpolationStatus &status) const
{
const static CAircraftSituation empty;
static const CAircraftSituation empty;
status.reset();
QList<CAircraftSituationList> splitSituations;
// any data at all?
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign");
if (this->remoteAircraftSituationsCount(callsign) < 1) { return empty; }
// data, split situations by time
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
qint64 splitTimeMsSinceEpoch = currentTimeMsSinceEpoc - TimeOffsetMs;
if (situationsPerCallsign)
{
// lock free, expected that situationsPerCallsign is a copy
if (!situationsPerCallsign->contains(callsign)) { return empty; }
if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; }
splitSituations = (*situationsPerCallsign)[callsign].splitByTime(splitTimeMsSinceEpoch);
}
else
{
// only part where it is locked
QReadLocker lock(&m_lockSituations);
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
if (m_situationsByCallsign[callsign].isEmpty()) { return empty; }
splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch, true);
}
QList<CAircraftSituationList> splitSituations(remoteAircraftSituations(callsign).splitByTime(splitTimeMsSinceEpoch, true));
CAircraftSituationList &situationsNewer = splitSituations[0]; // newer part
CAircraftSituationList &situationsOlder = splitSituations[1]; // older part
@@ -123,16 +113,15 @@ namespace BlackCore
// Interpolate altitude: Alt = (AltB - AltA) * t + AltA
const CAltitude oldAlt(oldSituation.getAltitude());
const CAltitude newAlt(newSituation.getAltitude());
Q_ASSERT(oldAlt.getReferenceDatum() == newAlt.getReferenceDatum()); // otherwise no calculation is possible
Q_ASSERT_X(oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(), Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
currentSituation.setAltitude(CAltitude((newAlt - oldAlt)
* simulationTimeFraction
+ oldAlt,
oldAlt.getReferenceDatum()));
if (newLat == oldLat && newLng == oldLng && oldAlt == newAlt)
if (!vtolAiracraft && newLat == oldLat && newLng == oldLng && oldAlt == newAlt)
{
// stop interpolation here
//! \todo Does not work for VTOL aircraft. We need a flag for VTOL aircraft
// stop interpolation here, does not work for VTOL aircraft. We need a flag for VTOL aircraft
return currentSituation;
}
@@ -179,7 +168,7 @@ namespace BlackCore
* simulationTimeFraction
+ oldSituation.getGroundSpeed());
status.changedPosition = true;
Q_ASSERT(currentSituation.getCallsign() == callsign);
Q_ASSERT_X(currentSituation.getCallsign() == callsign, Q_FUNC_INFO, "mismatching callsigns");
return currentSituation;
}

View File

@@ -31,7 +31,7 @@ namespace BlackCore
{}
//! \copydoc IInterpolator::getCurrentInterpolatedSituation
virtual BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc, InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign = nullptr) const override;
virtual BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc, bool vtolAiracraft, InterpolationStatus &status) const override;
//! Log category
static QString getMessageCategory() { return "swift.interpolatorlinear"; }

View File

@@ -42,7 +42,7 @@ namespace BlackCore
CRemoteAircraftAware(remoteAircraftProvider),
m_simulatorPluginInfo(info)
{
this->setObjectName("CSimulatorCommon");
this->setObjectName(info.getIdentifier());
m_oneSecondTimer = new QTimer(this);
m_oneSecondTimer->setObjectName(this->objectName().append(":OneSecondTimer"));
connect(this->m_oneSecondTimer, &QTimer::timeout, this, &CSimulatorCommon::ps_oneSecondTimer);
@@ -129,13 +129,13 @@ namespace BlackCore
if (!this->m_interpolator) { return; }
const CCallsign callsign(aircraft.getCallsign());
if (!this->m_interpolator->hasDataForCallsign(callsign)) { return; }
if (!(this->remoteAircraftSituationsCount(callsign) < 1)) { return; }
// with an interpolator the interpolated situation is used
// to avoid position jittering
qint64 time = QDateTime::currentMSecsSinceEpoch();
IInterpolator::InterpolationStatus is;
CAircraftSituation as(m_interpolator->getInterpolatedSituation(callsign, time, is));
CAircraftSituation as(m_interpolator->getInterpolatedSituation(callsign, time, aircraft.isVtol(), is));
if (is.interpolationSucceeded) { aircraft.setSituation(as); }
}