From 4975ecd7121efd953a58d186ff8d4d7782830604 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 8 Jun 2018 22:03:05 +0200 Subject: [PATCH] Ref T275, improved tracing for FSX * getStatisticsSimulatorSpecific for simulator specific traces/logs * trace CSimulatorFsxCommon::SimConnectProc times * trace which receive id is handled in SimConnectProc * allow to limit aircraft updates (max FPS) * handle airport updates outside SimConnectProc --- src/blackcore/simulator.h | 3 + src/blackcore/simulatorcommon.cpp | 79 +++++++++++-- src/blackcore/simulatorcommon.h | 23 +++- .../fsxcommon/simulatorfsxcommon.cpp | 105 ++++++++++++++++-- .../simulator/fsxcommon/simulatorfsxcommon.h | 39 ++++++- .../fsxcommon/simulatorfsxsimconnectproc.cpp | 36 +++--- src/plugins/simulator/p3d/simulatorp3d.cpp | 1 + 7 files changed, 242 insertions(+), 44 deletions(-) diff --git a/src/blackcore/simulator.h b/src/blackcore/simulator.h index 9ffd6983e..516495636 100644 --- a/src/blackcore/simulator.h +++ b/src/blackcore/simulator.h @@ -196,6 +196,9 @@ namespace BlackCore //! \remark needs to be overridden if the concrete driver supports such an option virtual bool requestElevation(const BlackMisc::Geo::ICoordinateGeodetic &reference, const BlackMisc::Aviation::CCallsign &callsign) override; + //! Allows to print out simulator specific statistics + virtual QString getStatisticsSimulatorSpecific() const { return QString(); } + //! \copydoc BlackMisc::IProvider::asQObject virtual QObject *asQObject() override { return this; } diff --git a/src/blackcore/simulatorcommon.cpp b/src/blackcore/simulatorcommon.cpp index c82013f1a..378187635 100644 --- a/src/blackcore/simulatorcommon.cpp +++ b/src/blackcore/simulatorcommon.cpp @@ -244,15 +244,15 @@ namespace BlackCore CAirportList CSimulatorCommon::getAirportsInRange() const { // default implementation + if (this->isShuttingDown()) { return CAirportList(); } if (!sApp || !sApp->hasWebDataServices()) { return CAirportList(); } - if (sApp->isShuttingDown()) { return CAirportList(); } - CAirportList airports = sApp->getWebDataServices()->getAirports(); + const CAirportList airports = sApp->getWebDataServices()->getAirports(); if (airports.isEmpty()) { return airports; } const CCoordinateGeodetic ownPosition = this->getOwnAircraftPosition(); - airports = airports.findClosest(maxAirportsInRange(), ownPosition); - if (m_autoCalcAirportDistance) { airports.calculcateAndUpdateRelativeDistanceAndBearing(ownPosition); } - return airports; + CAirportList airportInRange = airports.findClosest(maxAirportsInRange(), ownPosition); + if (m_autoCalcAirportDistance) { airportInRange.calculcateAndUpdateRelativeDistanceAndBearing(ownPosition); } + return airportInRange; } void CSimulatorCommon::setWeatherActivated(bool activated) @@ -276,6 +276,53 @@ namespace BlackCore return reverseModel; } + bool CSimulatorCommon::isUpdateAircraftLimited(qint64 timestamp) + { + if (!m_limitUpdateAircraft) { return false; } + const bool hasToken = m_limitUpdateAircraftBucket.tryConsume(1, timestamp); + return !hasToken; + } + + bool CSimulatorCommon::isUpdateAircraftLimitedWithStats(qint64 startTime) + { + const bool limited = this->isUpdateAircraftLimited(startTime); + this->setStatsRemoteAircraftUpdate(startTime, limited); + return limited; + } + + bool CSimulatorCommon::limitToUpdatesPerSecond(int numberPerSecond) + { + if (numberPerSecond < 1) + { + m_limitUpdateAircraft = false; + return false; + } + + int tokens = 0.1 * numberPerSecond; // 100ms + do + { + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(100); break; } + tokens = 0.25 * numberPerSecond; // 250ms + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(250); break; } + tokens = 0.5 * numberPerSecond; // 500ms + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(500); break; } + tokens = numberPerSecond; + m_limitUpdateAircraftBucket.setInterval(1000); + } + while (false); + + m_limitUpdateAircraftBucket.setCapacityAndTokensToRefill(tokens); + m_limitUpdateAircraft = true; + return true; + } + + QString CSimulatorCommon::updateAircraftLimitationInfo() const + { + if (!m_limitUpdateAircraft) { return QStringLiteral("not limited"); } + static const QString limInfo("Limited %1 times with %2/secs."); + return limInfo.arg(m_statsUpdateAircraftLimited).arg(m_limitUpdateAircraftBucket.getTokensPerSecond()); + } + void CSimulatorCommon::onSwiftDbAllDataRead() { // void, can be overridden in specialized drivers @@ -579,9 +626,18 @@ namespace BlackCore this->logicallyRemoveRemoteAircraft(cs); } } + return false; } + if (part1.startsWith("limit")) + { + const int perSecond = parser.toInt(2, -1); + this->limitToUpdatesPerSecond(perSecond); + CLogMessage(this).info("Remote aircraft updates limitations: %1") << this->updateAircraftLimitationInfo(); + return true; + } + // driver specific cmd line arguments return this->parseDetails(parser); } @@ -590,6 +646,7 @@ namespace BlackCore { if (CSimpleCommandParser::registered("BlackCore::CSimulatorCommon")) { return; } CSimpleCommandParser::registerCommand({".drv", "alias: .driver .plugin"}); + CSimpleCommandParser::registerCommand({".drv limit number/secs.", "limit updates to number per second (0..off)"}); CSimpleCommandParser::registerCommand({".drv logint callsign", "log interpolator for callsign"}); CSimpleCommandParser::registerCommand({".drv logint off", "no log information for interpolator"}); CSimpleCommandParser::registerCommand({".drv logint write", "write interpolator log to file"}); @@ -613,6 +670,7 @@ namespace BlackCore m_statsPhysicallyRemovedAircraft = 0; m_statsLastUpdateAircraftRequestedMs = 0; m_statsUpdateAircraftRequestedDeltaMs = 0; + m_statsUpdateAircraftLimited = 0; } CStatusMessageList CSimulatorCommon::debugVerifyStateAfterAllAircraftRemoved() const @@ -662,7 +720,7 @@ namespace BlackCore m_clampedLogMsg.remove(callsign); } - void CSimulatorCommon::setStatsRemoteAircraftUpdate(qint64 startTime) + void CSimulatorCommon::setStatsRemoteAircraftUpdate(qint64 startTime, bool limited) { const qint64 now = QDateTime::currentMSecsSinceEpoch(); const qint64 dt = now - startTime; @@ -671,9 +729,11 @@ namespace BlackCore m_statsUpdateAircraftRuns++; m_statsUpdateAircraftTimeAvgMs = static_cast(m_statsUpdateAircraftTimeTotalMs) / static_cast(m_statsUpdateAircraftRuns); m_updateRemoteAircraftInProgress = false; + m_statsLastUpdateAircraftRequestedMs = startTime; + if (m_statsMaxUpdateTimeMs < dt) { m_statsMaxUpdateTimeMs = dt; } if (m_statsLastUpdateAircraftRequestedMs > 0) { m_statsUpdateAircraftRequestedDeltaMs = startTime - m_statsLastUpdateAircraftRequestedMs; } - m_statsLastUpdateAircraftRequestedMs = startTime; + if (limited) { m_statsUpdateAircraftLimited++; } } bool CSimulatorCommon::isEqualLastSent(const CAircraftSituation &compare) const @@ -795,19 +855,22 @@ namespace BlackCore CAirportList CSimulatorCommon::getWebServiceAirports() const { + if (this->isShuttingDown()) { return CAirportList(); } if (!sApp->hasWebDataServices()) { return CAirportList(); } return sApp->getWebDataServices()->getAirports(); } CAirport CSimulatorCommon::getWebServiceAirport(const CAirportIcaoCode &icao) const { + if (this->isShuttingDown()) { return CAirport(); } if (!sApp->hasWebDataServices()) { return CAirport(); } return sApp->getWebDataServices()->getAirports().findFirstByIcao(icao); } void CSimulatorCommon::rapOnRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot) { - if (!this->isConnected()) return; + if (!this->isConnected()) { return; } + if (this->isShuttingDown()) { return; } this->onRecalculatedRenderedAircraft(snapshot); } diff --git a/src/blackcore/simulatorcommon.h b/src/blackcore/simulatorcommon.h index 7087cdf9d..952c4996e 100644 --- a/src/blackcore/simulatorcommon.h +++ b/src/blackcore/simulatorcommon.h @@ -33,6 +33,7 @@ #include "blackmisc/pq/length.h" #include "blackmisc/pq/time.h" #include "blackmisc/pq/units.h" +#include "blackmisc/tokenbucket.h" #include "blackmisc/connectionguard.h" namespace BlackMisc @@ -93,6 +94,7 @@ namespace BlackCore //! @{ //!
         //! .drv unload                    unload plugin                           BlackCore::CSimulatorCommon
+        //! .drv limit number              limit the number of updates             BlackCore::CSimulatorCommon
         //! .drv logint callsign           log interpolator for callsign           BlackCore::CSimulatorCommon
         //! .drv logint off                no log information for interpolator     BlackCore::CSimulatorCommon
         //! .drv logint write              write interpolator log to file          BlackCore::CSimulatorCommon
@@ -119,7 +121,7 @@ namespace BlackCore
         static void registerHelp();
 
         //! Reset the statistics counters
-        void resetAircraftStatistics();
+        virtual void resetAircraftStatistics();
 
         //!  Counter added aircraft
         int getStatisticsPhysicallyAddedAircraft() const { return m_statsPhysicallyAddedAircraft; }
@@ -152,6 +154,9 @@ namespace BlackCore
         //! \remark public only for log. displays
         QString latestLoggedDataFormatted(const BlackMisc::Aviation::CCallsign &cs) const;
 
+        //! Info about update aircraft limitations
+        QString updateAircraftLimitationInfo() const;
+
     protected:
         //! Constructor
         CSimulatorCommon(const BlackMisc::Simulation::CSimulatorPluginInfo &info,
@@ -246,7 +251,7 @@ namespace BlackCore
         void removedClampedLog(const BlackMisc::Aviation::CCallsign &callsign);
 
         //! Update stats and flags
-        void setStatsRemoteAircraftUpdate(qint64 startTime);
+        void setStatsRemoteAircraftUpdate(qint64 startTime, bool limited = false);
 
         //! Equal to last sent situation
         bool isEqualLastSent(const BlackMisc::Aviation::CAircraftSituation &compare) const;
@@ -268,6 +273,7 @@ namespace BlackCore
         bool   m_updateRemoteAircraftInProgress = false;  //!< currently updating remote aircraft
         int    m_timerId = -1;                            //!< dispatch timer id
         int    m_statsUpdateAircraftRuns = 0;             //!< statistics update count
+        int    m_statsUpdateAircraftLimited = 0;          //!< skipped because of max.update limitations
         double m_statsUpdateAircraftTimeAvgMs = 0;        //!< statistics average update time
         qint64 m_statsUpdateAircraftTimeTotalMs = 0;      //!< statistics total update time
         qint64 m_statsCurrentUpdateTimeMs = 0;            //!< statistics current update time
@@ -284,6 +290,19 @@ namespace BlackCore
         // some optional functionality which can be used by the simulators as needed
         BlackMisc::Simulation::CSimulatedAircraftList m_addAgainAircraftWhenRemoved; //!< add this model again when removed, normally used to change model
 
+        // limit the update aircraft to a maximum per second
+        BlackMisc::CTokenBucket m_limitUpdateAircraftBucket { 5, 100, 5 }; //!< means 50 per second
+        bool m_limitUpdateAircraft = false; //!< limit the update frequency by using BlackMisc::CTokenBucket
+
+        //! Limit reached (max number of updates by token bucket if enabled)
+        bool isUpdateAircraftLimited(qint64 timestamp = -1);
+
+        //! Limited as CSimulatorCommon::isUpdateAircraftLimited plus updating statistics
+        bool isUpdateAircraftLimitedWithStats(qint64 startTime = -1);
+
+        //! Limit to updates per seconds
+        bool limitToUpdatesPerSecond(int numberPerSecond);
+
         // weather
         bool m_isWeatherActivated = false;                         //!< Is simulator weather activated?
         BlackMisc::Geo::CCoordinateGeodetic m_lastWeatherPosition; //!< Own aircraft position at which weather was fetched and injected last
diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
index 34e1653e1..486ed9e37 100644
--- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
+++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
@@ -255,6 +255,16 @@ namespace BlackSimPlugin
             return msgs;
         }
 
+        QString CSimulatorFsxCommon::getStatisticsSimulatorSpecific() const
+        {
+            static const QString specificInfo("dispatch (cur/max): %1ms %2ms %3 %4 simData#: %5");
+            return specificInfo.
+                   arg(m_dispatchTimeMs).arg(m_dispatchMaxTimeMs).
+                   arg(CSimConnectUtilities::simConnectReceiveIdToString(m_dispatchMaxTimeReceiveId),
+                       CSimConnectDefinitions::requestToString(m_dispatchMaxTimeRequest)).
+                   arg(m_requestSimObjectDataCount);
+        }
+
         bool CSimulatorFsxCommon::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &callsign)
         {
             Q_UNUSED(callsign);
@@ -304,12 +314,29 @@ namespace BlackSimPlugin
             m_traceAutoTs = -1;
         }
 
+        void CSimulatorFsxCommon::resetAircraftStatistics()
+        {
+            m_dispatchMaxTimeMs = -1;
+            m_dispatchTimeMs = -1;
+            m_requestSimObjectDataCount = 0;
+            m_dispatchLastReceiveId = SIMCONNECT_RECV_ID_NULL;
+            m_dispatchMaxTimeReceiveId = SIMCONNECT_RECV_ID_NULL;
+            m_dispatchLastRequest = CSimConnectDefinitions::RequestEndMarker;
+            m_dispatchMaxTimeRequest = CSimConnectDefinitions::RequestEndMarker;
+            CSimulatorPluginCommon::resetAircraftStatistics();
+        }
+
         bool CSimulatorFsxCommon::stillDisplayReceiveExceptions()
         {
             m_receiveExceptionCount++;
             return m_receiveExceptionCount < IgnoreReceiveExceptions;
         }
 
+        CSimConnectObject CSimulatorFsxCommon::getSimObjectForObjectId(DWORD objectId) const
+        {
+            return this->getSimConnectObjects().getSimObjectForObjectId(objectId);
+        }
+
         void CSimulatorFsxCommon::setSimConnected()
         {
             m_simConnected = true;
@@ -387,11 +414,7 @@ namespace BlackSimPlugin
 
         void CSimulatorFsxCommon::onSimFrame()
         {
-            if (m_updateRemoteAircraftInProgress)
-            {
-                return;
-            }
-
+            if (m_updateRemoteAircraftInProgress) { return; }
             QPointer myself(this);
             QTimer::singleShot(0, this, [ = ]
             {
@@ -551,7 +574,7 @@ namespace BlackSimPlugin
         void CSimulatorFsxCommon::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftSimData &remoteAircraftData)
         {
             // Near ground we use faster updates
-            if (remoteAircraftData.aboveGround() <= 100.0)
+            if (remoteAircraftData.aboveGroundFt() <= 100.0)
             {
                 // switch to fast updates
                 if (simObject.getSimDataPeriod() != SIMCONNECT_PERIOD_VISUAL_FRAME)
@@ -570,12 +593,12 @@ namespace BlackSimPlugin
 
             // CElevationPlane: deg, deg, feet
             // we only remember near ground
-            const CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupPerCallsignOrDefault(simObject.getCallsign());
-            if (simObject.getLastInterpolatedSituation(setup.getInterpolatorMode()).canLikelySkipNearGroundInterpolation()) { return; }
-
-            CElevationPlane elevation(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg, remoteAircraftData.elevationFt);
-            elevation.setSinglePointRadius();
-            this->rememberElevationAndCG(simObject.getCallsign(), elevation, CLength(remoteAircraftData.cgToGroundFt, CLengthUnit::ft()));
+            if (remoteAircraftData.aboveGroundFt() < 250)
+            {
+                CElevationPlane elevation(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg, remoteAircraftData.elevationFt);
+                elevation.setSinglePointRadius();
+                this->rememberElevationAndCG(simObject.getCallsign(), elevation, CLength(remoteAircraftData.cgToGroundFt, CLengthUnit::ft()));
+            }
         }
 
         void CSimulatorFsxCommon::updatProbeFromSimulator(const CCallsign &callsign, const DataDefinitionRemoteAircraftSimData &remoteAircraftData)
@@ -769,6 +792,7 @@ namespace BlackSimPlugin
 
         bool CSimulatorFsxCommon::simulatorReportedObjectRemoved(DWORD objectID)
         {
+            if (this->isShuttingDown()) { return false; }
             const CSimConnectObject simObject = m_simConnectObjects.getSimObjectForObjectId(objectID);
             if (!simObject.hasValidRequestAndObjectId()) { return false; } // object id from somewhere else
             const CCallsign callsign(simObject.getCallsign());
@@ -908,10 +932,29 @@ namespace BlackSimPlugin
         {
             // call CSimulatorFsxCommon::SimConnectProc or specialized P3D version
             Q_ASSERT_X(m_dispatchProc, Q_FUNC_INFO, "Missing DispatchProc");
+
+            // statistics
+            const qint64 start = QDateTime::currentMSecsSinceEpoch();
+            m_dispatchLastReceiveId = SIMCONNECT_RECV_ID_NULL;
+            m_dispatchLastRequest = CSimConnectDefinitions::RequestEndMarker;
+
+            // process
             const HRESULT hr = SimConnect_CallDispatch(m_hSimConnect, m_dispatchProc, this);
+
+            // statistics
+            m_dispatchTimeMs = QDateTime::currentMSecsSinceEpoch() - start;
+            if (m_dispatchMaxTimeMs < m_dispatchTimeMs)
+            {
+                m_dispatchMaxTimeMs = m_dispatchTimeMs;
+                m_dispatchMaxTimeReceiveId = m_dispatchLastReceiveId;
+                m_dispatchMaxTimeRequest = m_dispatchLastRequest;
+            }
+
+            // error handling
             if (hr != S_OK)
             {
                 m_dispatchErrors++;
+                this->triggerAutoTraceSendId();
                 if (m_dispatchErrors == 2)
                 {
                     // 2nd time, an error / avoid multiple messages
@@ -1290,6 +1333,7 @@ namespace BlackSimPlugin
 
             // values used for position and parts
             const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
+            if (this->isUpdateAircraftLimitedWithStats(currentTimestamp)) { return; }
             m_updateRemoteAircraftInProgress = true;
 
             // interpolation for all remote aircraft
@@ -1364,6 +1408,40 @@ namespace BlackSimPlugin
             return this->sendRemoteAircraftPartsToSimulator(simObject, ddRemoteAircraftPartsWithoutLights, parts.getAdjustedLights());
         }
 
+        void CSimulatorFsxCommon::triggerUpdateAirports(const CAirportList &airports)
+        {
+            if (this->isShuttingDown()) { return; }
+            if (airports.isEmpty()) { return; }
+            QPointer myself(this);
+            QTimer::singleShot(0, this, [ = ]
+            {
+                if (!myself) { return; }
+                this->updateAirports(airports);
+            });
+        }
+
+        void CSimulatorFsxCommon::updateAirports(const CAirportList &airports)
+        {
+            if (airports.isEmpty()) { return; }
+
+            static const CLength maxDistance(200.0, CLengthUnit::NM());
+            const CCoordinateGeodetic posAircraft(this->getOwnAircraftPosition());
+
+            for (const CAirport &airport : airports)
+            {
+                CAirport consolidatedAirport(airport);
+                const CLength d = consolidatedAirport.calculcateAndUpdateRelativeDistanceAndBearing(posAircraft);
+                if (d > maxDistance) { continue; }
+                consolidatedAirport.updateMissingParts(this->getWebServiceAirport(airport.getIcao()));
+                m_airportsInRangeFromSimulator.replaceOrAddByIcao(consolidatedAirport);
+                if (m_airportsInRangeFromSimulator.size() > this->maxAirportsInRange())
+                {
+                    m_airportsInRangeFromSimulator.sortByDistanceToOwnAircraft();
+                    m_airportsInRangeFromSimulator.truncate(this->maxAirportsInRange());
+                }
+            }
+        }
+
         bool CSimulatorFsxCommon::sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject, DataDefinitionRemoteAircraftPartsWithoutLights &ddRemoteAircraftPartsWithoutLights, const CAircraftLights &lights)
         {
             Q_ASSERT(m_hSimConnect);
@@ -1563,10 +1641,13 @@ namespace BlackSimPlugin
 
             if (result == S_OK && m_simConnectObjects.contains(simObject.getCallsign()))
             {
+                m_requestSimObjectDataCount++;
                 if (this->isTracingSendId()) { this->traceSendId(simObject.getObjectId(), Q_FUNC_INFO);}
                 m_simConnectObjects[simObject.getCallsign()].setSimDataPeriod(period);
                 return true;
             }
+
+            // failure
             CLogMessage(this).error("Cannot request simulator data on object '%1'") << simObject.getObjectId();
             return false;
         }
diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h
index e205b96fd..5a1cfc84a 100644
--- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h
+++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h
@@ -129,6 +129,7 @@ namespace BlackSimPlugin
             virtual BlackMisc::Aviation::CCallsignSet physicallyRenderedAircraft() const override;
             virtual void clearAllRemoteAircraftData() override;
             virtual BlackMisc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const override;
+            virtual QString getStatisticsSimulatorSpecific() const override;
             //! @}
 
             //! \copydoc BlackMisc::Simulation::ISimulationEnvironmentProvider::requestElevation
@@ -140,8 +141,12 @@ namespace BlackSimPlugin
             //! Set tracing on/off
             void setTractingSendId(bool trace);
 
+            //! \copydoc BlackCore::CSimulatorCommon::resetAircraftStatistics
+            virtual void resetAircraftStatistics() override;
+
         protected:
-            //! SimConnect Callback
+            //! SimConnect callback
+            //! \note all tasks called in this function (i.e, all called functions) must perform fast or shall be called asynchronously
             static void CALLBACK SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext);
 
             //! \name Interface implementations
@@ -278,6 +283,12 @@ namespace BlackSimPlugin
             //! Update remote aircraft parts (send to FSX)
             bool updateRemoteAircraftParts(const CSimConnectObject &simObject, const BlackMisc::Simulation::CInterpolationResult &result);
 
+            //! Calling CSimulatorFsxCommon::updateAirports
+            void triggerUpdateAirports(const BlackMisc::Aviation::CAirportList &airports);
+
+            //! Update airports from simulator
+            void updateAirports(const BlackMisc::Aviation::CAirportList &airports);
+
             //! Send parts to simulator
             //! \remark does not send if there is no change
             bool sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject, DataDefinitionRemoteAircraftPartsWithoutLights &ddRemoteAircraftParts, const BlackMisc::Aviation::CAircraftLights &lights);
@@ -323,9 +334,12 @@ namespace BlackSimPlugin
             //! Display receive exceptions?
             bool stillDisplayReceiveExceptions();
 
-            //! The simconnect related objects
+            //! The SimConnect related objects
             const CSimConnectObjects &getSimConnectObjects() const { return m_simConnectObjects; }
 
+            //! The SimConnect object for idxs
+            CSimConnectObject getSimObjectForObjectId(DWORD objectId) const;
+
             //! The simconnect related probes
             const CSimConnectObjects &getSimConnectProbes() const { return m_simConnectProbes; }
 
@@ -400,16 +414,29 @@ namespace BlackSimPlugin
             qint64 m_simulatingChangedTs = -1;      //!< timestamp, when simulating changed (used to avoid jitter)
             int m_syncDeferredCounter =  0;         //!< Set when synchronized, used to wait some time
             int m_skipCockpitUpdateCycles = 0;      //!< skip some update cycles to allow changes in simulator cockpit to be set
+
+            // tracing dispatch performance
             int m_dispatchErrors = 0;               //!< number of dispatched failed, \sa dispatch
-            int m_receiveExceptionCount = 0;        //!< exceptions
-            QList m_sendIdTraces;   //!< Send id traces for debugging
+            qint64 m_dispatchTimeMs = -1;
+            qint64 m_dispatchMaxTimeMs = -1;
+            SIMCONNECT_RECV_ID m_dispatchLastReceiveId    = SIMCONNECT_RECV_ID_NULL; //!< last receive id from dispatching
+            SIMCONNECT_RECV_ID m_dispatchMaxTimeReceiveId = SIMCONNECT_RECV_ID_NULL; //!< receive id corresponding to max.time
+            CSimConnectDefinitions::Request m_dispatchLastRequest    = CSimConnectDefinitions::RequestEndMarker; //!< request id if any
+            CSimConnectDefinitions::Request m_dispatchMaxTimeRequest = CSimConnectDefinitions::RequestEndMarker; //!< request id corresponding to max.time
+
+            // sending via SimConnect
+            QList m_sendIdTraces; //!< Send id traces for debugging
+            int m_receiveExceptionCount = 0;      //!< exceptions
+            int m_requestSimObjectDataCount  = 0; //!< requested SimObjects
+
+            // objects
             CSimConnectObjects m_simConnectObjects; //!< AI objects and their object / request ids
             CSimConnectObjects m_simConnectProbes;  //!< AI terrain probes
             CSimConnectObjects m_simConnectObjectsPositionAndPartsTraces; //!< position/parts received, but object not yet added, excluded, disabled etc.
             SIMCONNECT_DATA_REQUEST_ID m_requestIdSimData = static_cast(RequestIdSimDataStart);    //!< request id, use obtainRequestIdForSimData() to get id
-            SIMCONNECT_DATA_REQUEST_ID m_requestIdProbe = static_cast(RequestIdTerrainProbeStart); //!< request id, use obtainRequestIdForSimData() to get id
+            SIMCONNECT_DATA_REQUEST_ID m_requestIdProbe = static_cast(RequestIdTerrainProbeStart); //!< request id, use obtainRequestIdForProbe() to get id
             BlackMisc::Simulation::CSimulatedAircraftList m_addPendingAircraft; //!< aircraft awaiting to be added
-            QTimer m_addPendingSimObjTimer; //!< updating of sim objects awaiting to be added
+            QTimer m_addPendingSimObjTimer; //!< updating of SimObjects awaiting to be added
         };
 
         //! Listener for FSX
diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp
index e4483e65d..e8c9d0eb7 100644
--- a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp
+++ b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp
@@ -33,8 +33,13 @@ namespace BlackSimPlugin
     {
         void CALLBACK CSimulatorFsxCommon::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)
         {
+            // IMPORTANT:
+            // all tasks called in this function (ie all called functions) must perform fast or shall be called asynchronously
+
             CSimulatorFsxCommon *simulatorFsxP3D = static_cast(pContext);
-            switch (pData->dwID)
+            const SIMCONNECT_RECV_ID recvId = static_cast(pData->dwID);
+            simulatorFsxP3D->m_dispatchLastReceiveId = recvId;
+            switch (recvId)
             {
             case SIMCONNECT_RECV_ID_OPEN:
                 {
@@ -210,11 +215,13 @@ namespace BlackSimPlugin
                 {
                     const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA *) pData;
                     const DWORD requestId = pObjData->dwRequestID;
+
                     switch (requestId)
                     {
                     case CSimConnectDefinitions::RequestOwnAircraft:
                         {
                             static_assert(sizeof(DataDefinitionOwnAircraft) == 31 * sizeof(double), "DataDefinitionOwnAircraft has an incorrect size.");
+                            simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestOwnAircraft;
                             const DataDefinitionOwnAircraft *ownAircaft = (DataDefinitionOwnAircraft *)&pObjData->dwData;
                             simulatorFsxP3D->updateOwnAircraftFromSimulator(*ownAircaft);
                             break;
@@ -222,14 +229,14 @@ namespace BlackSimPlugin
                     case CSimConnectDefinitions::RequestOwnAircraftTitle:
                         {
                             const DataDefinitionOwnAircraftModel *dataDefinitionModel = (DataDefinitionOwnAircraftModel *) &pObjData->dwData;
-                            CAircraftModel model;
-                            model.setModelString(dataDefinitionModel->title);
-                            model.setModelType(CAircraftModel::TypeOwnSimulatorModel);
+                            simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestOwnAircraftTitle;
+                            const CAircraftModel model(dataDefinitionModel->title, CAircraftModel::TypeOwnSimulatorModel);
                             simulatorFsxP3D->reverseLookupAndUpdateOwnAircraftModel(model);
                             break;
                         }
                     case CSimConnectDefinitions::RequestSimEnvironment:
                         {
+                            simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestSimEnvironment;
                             const DataDefinitionSimEnvironment *simEnv = (DataDefinitionSimEnvironment *) &pObjData->dwData;
                             if (simulatorFsxP3D->isTimeSynchronized())
                             {
@@ -249,7 +256,8 @@ namespace BlackSimPlugin
                             if (CSimulatorFsxCommon::isRequestForSimData(requestId))
                             {
                                 static_assert(sizeof(DataDefinitionRemoteAircraftSimData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
-                                const CSimConnectObject simObj = simulatorFsxP3D->getSimConnectObjects().getSimObjectForObjectId(objectId);
+                                simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForSimData;
+                                const CSimConnectObject simObj = simulatorFsxP3D->getSimObjectForObjectId(objectId);
                                 if (!simObj.hasValidRequestAndObjectId()) { break; }
                                 const DataDefinitionRemoteAircraftSimData *remoteAircraftSimData = (DataDefinitionRemoteAircraftSimData *)&pObjData->dwData;
                                 // extra check, but ids should be the same
@@ -261,6 +269,7 @@ namespace BlackSimPlugin
                             else if (CSimulatorFsxCommon::isRequestForProbe(requestId))
                             {
                                 static_assert(sizeof(DataDefinitionRemoteAircraftSimData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
+                                simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForProbe;
                                 const CSimConnectObject probeObj = simulatorFsxP3D->getSimConnectProbes().getSimObjectForObjectId(objectId);
                                 if (!probeObj.hasValidRequestAndObjectId()) { break; }
                                 const DataDefinitionRemoteAircraftSimData *probeSimData = (DataDefinitionRemoteAircraftSimData *)&pObjData->dwData;
@@ -275,6 +284,7 @@ namespace BlackSimPlugin
                             else if (CSimulatorFsxCommon::isRequestForLights(requestId))
                             {
                                 static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double), "DataDefinitionRemoteAircraftLights has an incorrect size.");
+                                simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForLights;
                                 const CSimConnectObject simObj = simulatorFsxP3D->getSimConnectObjects().getSimObjectForObjectId(objectId);
                                 if (!simObj.hasValidRequestAndObjectId()) break;
                                 const DataDefinitionRemoteAircraftLights *remoteAircraftLights = (DataDefinitionRemoteAircraftLights *)&pObjData->dwData;
@@ -299,9 +309,8 @@ namespace BlackSimPlugin
                 }
             case SIMCONNECT_RECV_ID_AIRPORT_LIST:
                 {
-                    static const CLength maxDistance(200.0, CLengthUnit::NM());
-                    const CCoordinateGeodetic posAircraft(simulatorFsxP3D->getOwnAircraftPosition());
                     const SIMCONNECT_RECV_AIRPORT_LIST *pAirportList = (SIMCONNECT_RECV_AIRPORT_LIST *) pData;
+                    CAirportList simAirports;
                     for (unsigned i = 0; i < pAirportList->dwArraySize; ++i)
                     {
                         const SIMCONNECT_DATA_FACILITY_AIRPORT *pFacilityAirport = pAirportList->rgData + i;
@@ -311,17 +320,12 @@ namespace BlackSimPlugin
                         if (!CAirportIcaoCode::isValidIcaoDesignator(icao)) { continue; } // tiny airfields/strips in simulator
                         if (CAirportIcaoCode::containsNumbers(icao)) { continue; } // tiny airfields/strips in simulator
                         const CCoordinateGeodetic pos(pFacilityAirport->Latitude, pFacilityAirport->Longitude, pFacilityAirport->Altitude);
-                        CAirport airport(CAirportIcaoCode(icao), pos);
-                        const CLength d = airport.calculcateAndUpdateRelativeDistanceAndBearing(posAircraft);
-                        if (d > maxDistance) { continue; }
-                        airport.updateMissingParts(simulatorFsxP3D->getWebServiceAirport(icao));
-                        simulatorFsxP3D->m_airportsInRangeFromSimulator.replaceOrAddByIcao(airport);
+                        const CAirport airport(CAirportIcaoCode(icao), pos);
+                        simAirports.push_back(airport);
                     }
-
-                    if (simulatorFsxP3D->m_airportsInRangeFromSimulator.size() > simulatorFsxP3D->maxAirportsInRange())
+                    if (!simAirports.isEmpty())
                     {
-                        simulatorFsxP3D->m_airportsInRangeFromSimulator.sortByDistanceToOwnAircraft();
-                        simulatorFsxP3D->m_airportsInRangeFromSimulator.truncate(simulatorFsxP3D->maxAirportsInRange());
+                        simulatorFsxP3D->triggerUpdateAirports(simAirports); // real "work" outside SimConnectProc
                     }
                     break; // SIMCONNECT_RECV_ID_AIRPORT_LIST
                 }
diff --git a/src/plugins/simulator/p3d/simulatorp3d.cpp b/src/plugins/simulator/p3d/simulatorp3d.cpp
index b6c6cfb22..f61be620f 100644
--- a/src/plugins/simulator/p3d/simulatorp3d.cpp
+++ b/src/plugins/simulator/p3d/simulatorp3d.cpp
@@ -35,6 +35,7 @@ namespace BlackSimPlugin
                                      QObject *parent) :
             CSimulatorFsxCommon(info, ownAircraftProvider, remoteAircraftProvider, weatherGridProvider, clientProvider, parent)
         {
+            // set build/sim specific SimConnectProc, which is the FSX SimConnectProc on WIN32 systems
             if (CBuildConfig::isCompiledWithP3DSupport() && CBuildConfig::isRunningOnWindowsNtPlatform() && CBuildConfig::buildWordSize() == 64)
             {
                 m_dispatchProc = &CSimulatorP3D::SimConnectProc;