From 62d4a94106a6d672da942556388bae82b9534123 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sat, 28 Jan 2017 02:58:55 +0100 Subject: [PATCH] refs #865, further improved logging * set log flag in hints, so no lock for each aircraft is needed * as a result log functions have a bool log parameter now * highlight situation and parts changed --- resources/share/html/swifttemplate.html | 1 + src/blackmisc/aviation/aircraftengine.cpp | 6 +- src/blackmisc/aviation/aircraftengine.h | 1 - src/blackmisc/simulation/interpolationhints.h | 18 ++- src/blackmisc/simulation/interpolator.cpp | 145 +++++++++++------- src/blackmisc/simulation/interpolator.h | 22 +-- .../simulation/interpolatorlinear.cpp | 4 +- .../simulation/remoteaircraftprovider.cpp | 1 + .../simulation/remoteaircraftprovider.h | 2 +- src/plugins/simulator/fs9/fs9client.cpp | 3 +- src/plugins/simulator/fsx/simulatorfsx.cpp | 8 +- 11 files changed, 133 insertions(+), 78 deletions(-) diff --git a/resources/share/html/swifttemplate.html b/resources/share/html/swifttemplate.html index 13c99b636..b25f65c87 100644 --- a/resources/share/html/swifttemplate.html +++ b/resources/share/html/swifttemplate.html @@ -43,6 +43,7 @@ a:hover + .mouseoverdisplay { .old { color: blue; } .new { color: green; } .cur { color: red; } +.changed { background-color: yellow; } diff --git a/src/blackmisc/aviation/aircraftengine.cpp b/src/blackmisc/aviation/aircraftengine.cpp index 729688e6e..898f163b8 100644 --- a/src/blackmisc/aviation/aircraftengine.cpp +++ b/src/blackmisc/aviation/aircraftengine.cpp @@ -19,19 +19,19 @@ namespace BlackMisc { CAircraftEngine::CAircraftEngine(int number, bool on) : m_number(number), m_on(on) { - Q_ASSERT_X(number > 0, "CAircraftEngine", "Engine number have to be > 1"); + Q_ASSERT_X(number > 0, "CAircraftEngine", "Engine numbers have to be > 1"); } void CAircraftEngine::setNumber(int number) { - Q_ASSERT_X(number > 0, "setNumber", "Engine number have to be > 1"); + Q_ASSERT_X(number > 0, "setNumber", "Engine numbers have to be > 1"); m_number = number; } QString CAircraftEngine::convertToQString(bool i18n) const { Q_UNUSED(i18n); - static const QString s("%1 on: %2"); + static const QString s("%1: %2"); return s.arg(m_number).arg(BlackMisc::boolToOnOff(m_on)); } } // namespace diff --git a/src/blackmisc/aviation/aircraftengine.h b/src/blackmisc/aviation/aircraftengine.h index 2f08b1c82..61f23d407 100644 --- a/src/blackmisc/aviation/aircraftengine.h +++ b/src/blackmisc/aviation/aircraftengine.h @@ -27,7 +27,6 @@ namespace BlackMisc class BLACKMISC_EXPORT CAircraftEngine : public CValueObject { public: - //! Default constructor CAircraftEngine() {} diff --git a/src/blackmisc/simulation/interpolationhints.h b/src/blackmisc/simulation/interpolationhints.h index 23713bab3..de1e54ae3 100644 --- a/src/blackmisc/simulation/interpolationhints.h +++ b/src/blackmisc/simulation/interpolationhints.h @@ -68,6 +68,12 @@ namespace BlackMisc //! VTOL aircraft void setVtolAircraft(bool vtol) { m_isVtol = vtol; } + //! Log interpolation? + bool isLoggingInterpolation() const { return m_logInterpolation; } + + //! Log interpolation? + void setLoggingInterpolation(bool log) { m_logInterpolation = log; } + //! Has valid aircraft parts? bool hasAircraftParts() const { return m_hasParts; } @@ -101,11 +107,12 @@ namespace BlackMisc QString debugInfo(const BlackMisc::Geo::CElevationPlane &deltaElevation) const; private: - bool m_isVtol = false; //!< VTOL aircraft? - bool m_hasParts = false; //!< Has valid aircraft parts? + bool m_isVtol = false; //!< VTOL aircraft? + bool m_hasParts = false; //!< Has valid aircraft parts? + bool m_logInterpolation = false; //!< log interpolation BlackMisc::Aviation::CAircraftParts m_aircraftParts; //!< Aircraft parts - BlackMisc::Geo::CElevationPlane m_elevation; //!< aircraft's elevation if available - ElevationProvider m_elevationProvider; //!< Provider of ground elevation (lazy computation) + BlackMisc::Geo::CElevationPlane m_elevation; //!< aircraft's elevation if available + ElevationProvider m_elevationProvider; //!< Provider of ground elevation (lazy computation) BlackMisc::PhysicalQuantities::CLength m_cgAboveGround { 0, nullptr }; //!< center of gravity above ground BLACK_METACLASS( @@ -114,7 +121,8 @@ namespace BlackMisc BLACK_METAMEMBER(elevation), BLACK_METAMEMBER(cgAboveGround), BLACK_METAMEMBER(hasParts), - BLACK_METAMEMBER(aircraftParts) + BLACK_METAMEMBER(aircraftParts), + BLACK_METAMEMBER(logInterpolation) // elevationProvider not included ); }; diff --git a/src/blackmisc/simulation/interpolator.cpp b/src/blackmisc/simulation/interpolator.cpp index ec717faf9..716dfe032 100644 --- a/src/blackmisc/simulation/interpolator.cpp +++ b/src/blackmisc/simulation/interpolator.cpp @@ -52,12 +52,13 @@ namespace BlackMisc return currentSituation; } - CAircraftParts IInterpolator::getInterpolatedParts(const CCallsign &callsign, const CAircraftPartsList &parts, qint64 currentTimeMsSinceEpoch, IInterpolator::PartsStatus &partsStatus) const + CAircraftParts IInterpolator::getInterpolatedParts(const CCallsign &callsign, const CAircraftPartsList &parts, qint64 currentTimeMsSinceEpoch, IInterpolator::PartsStatus &partsStatus, bool log) const { partsStatus.reset(); if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); } // find the first parts not in the correct order, keep only the parts before that one + if (parts.isEmpty()) { return {}; } const auto end = std::is_sorted_until(parts.begin(), parts.end(), [](auto && a, auto && b) { return b.getAdjustedMSecsSinceEpoch() < a.getAdjustedMSecsSinceEpoch(); }); const auto validParts = makeRange(parts.begin(), end); @@ -65,42 +66,46 @@ namespace BlackMisc if (validParts.isEmpty()) { return {}; } partsStatus.setSupportsParts(true); - // find the first parts earlier than the current time - const auto pivot = std::partition_point(validParts.begin(), validParts.end(), [ = ](auto && p) { return p.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoch; }); - const auto partsNewer = makeRange(validParts.begin(), pivot).reverse(); - const auto partsOlder = makeRange(pivot, validParts.end()); + CAircraftParts currentParts; + do + { + // find the first parts earlier than the current time + const auto pivot = std::partition_point(validParts.begin(), validParts.end(), [ = ](auto && p) { return p.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoch; }); + const auto partsNewer = makeRange(validParts.begin(), pivot).reverse(); + const auto partsOlder = makeRange(pivot, validParts.end()); - if (partsOlder.isEmpty()) { return *(partsNewer.end() - 1); } - CAircraftParts currentParts = partsOlder.front(); - if (currentParts.isOnGround()) { return currentParts; } + if (partsOlder.isEmpty()) { currentParts = *(partsNewer.end() - 1); break; } + currentParts = partsOlder.front(); + if (currentParts.isOnGround()) { break; } - // here we know aircraft is not on ground, and we check if it was recently on ground or if it will be on ground soon - const auto latestTakeoff = std::adjacent_find(partsOlder.begin(), partsOlder.end(), [](auto &&, auto && p) { return p.isOnGround(); }); - const auto soonestLanding = std::find_if(partsNewer.begin(), partsNewer.end(), [](auto && p) { return p.isOnGround(); }); + // here we know aircraft is not on ground, and we check if it was recently on ground or if it will be on ground soon + const auto latestTakeoff = std::adjacent_find(partsOlder.begin(), partsOlder.end(), [](auto &&, auto && p) { return p.isOnGround(); }); + const auto soonestLanding = std::find_if(partsNewer.begin(), partsNewer.end(), [](auto && p) { return p.isOnGround(); }); - // maxSecs is the maximum effective value of `secondsSinceTakeoff` and `secondsUntilLanding`. If `secondsSinceTakeoff > significantPast` then `takeoffFactor > 1` - // and if `secondsUntilLanding > predictableFuture` then `landingFactor > 1`, and `std::min(std::min(takeoffFactor, landingFactor), 1.0)` ensures `>1` is ignored. - // but if the offset < 5s then we must use a smaller value for the landing, hence `std::min(max, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0)`. - const double maxSecs = 5.0; // preferred length of time over which to blend the onground flag, when possible + // maxSecs is the maximum effective value of `secondsSinceTakeoff` and `secondsUntilLanding`. If `secondsSinceTakeoff > significantPast` then `takeoffFactor > 1` + // and if `secondsUntilLanding > predictableFuture` then `landingFactor > 1`, and `std::min(std::min(takeoffFactor, landingFactor), 1.0)` ensures `>1` is ignored. + // but if the offset < 5s then we must use a smaller value for the landing, hence `std::min(max, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0)`. + const double maxSecs = 5.0; // preferred length of time over which to blend the onground flag, when possible - // our clairvoyance is limited by the time offset (all times here in seconds) - const double significantPastSecs = maxSecs; - const double predictableFutureSecs = soonestLanding == partsNewer.end() ? maxSecs : std::min(maxSecs, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0); - const double secondsSinceTakeoff = latestTakeoff == partsOlder.end() ? maxSecs : (currentTimeMsSinceEpoch - latestTakeoff->getAdjustedMSecsSinceEpoch()) / 1000.0; - const double secondsUntilLanding = soonestLanding == partsNewer.end() ? maxSecs : (soonestLanding->getAdjustedMSecsSinceEpoch() - currentTimeMsSinceEpoch) / 1000.0; - Q_ASSERT(secondsSinceTakeoff >= 0.0); - Q_ASSERT(secondsUntilLanding >= 0.0); + // our clairvoyance is limited by the time offset (all times here in seconds) + const double significantPastSecs = maxSecs; + const double predictableFutureSecs = soonestLanding == partsNewer.end() ? maxSecs : std::min(maxSecs, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0); + const double secondsSinceTakeoff = latestTakeoff == partsOlder.end() ? maxSecs : (currentTimeMsSinceEpoch - latestTakeoff->getAdjustedMSecsSinceEpoch()) / 1000.0; + const double secondsUntilLanding = soonestLanding == partsNewer.end() ? maxSecs : (soonestLanding->getAdjustedMSecsSinceEpoch() - currentTimeMsSinceEpoch) / 1000.0; + Q_ASSERT(secondsSinceTakeoff >= 0.0); + Q_ASSERT(secondsUntilLanding >= 0.0); - const double takeoffFactor = secondsSinceTakeoff / significantPastSecs; - const double landingFactor = secondsUntilLanding / predictableFutureSecs; - const double airborneFactor = std::min(std::min(takeoffFactor, landingFactor), 1.0); - currentParts.setOnGroundInterpolated(1.0 - smootherStep(airborneFactor)); + const double takeoffFactor = secondsSinceTakeoff / significantPastSecs; + const double landingFactor = secondsUntilLanding / predictableFutureSecs; + const double airborneFactor = std::min(std::min(takeoffFactor, landingFactor), 1.0); + currentParts.setOnGroundInterpolated(1.0 - smootherStep(airborneFactor)); + } + while (false); - - const CInterpolationAndRenderingSetup setup = this->getInterpolatorSetup(); - if (setup.getLogCallsigns().contains(callsign)) + if (log) { PartsLog log; + log.callsign = callsign; log.timestamp = currentTimeMsSinceEpoch; log.parts = currentParts; IInterpolator::logParts(log); @@ -109,14 +114,11 @@ namespace BlackMisc return currentParts; } - CAircraftParts IInterpolator::getInterpolatedParts(const CCallsign &callsign, qint64 currentTimeMsSinceEpoch, IInterpolator::PartsStatus &partsStatus) const + CAircraftParts IInterpolator::getInterpolatedParts(const CCallsign &callsign, qint64 currentTimeMsSinceEpoch, IInterpolator::PartsStatus &partsStatus, bool log) const { Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign"); partsStatus.reset(); - - partsStatus.setSupportsParts(this->isRemoteAircraftSupportingParts(callsign)); - if (!partsStatus.isSupportingParts()) { return {}; } - return this->getInterpolatedParts(callsign, this->remoteAircraftParts(callsign, -1), currentTimeMsSinceEpoch, partsStatus); + return this->getInterpolatedParts(callsign, this->remoteAircraftParts(callsign, -1), currentTimeMsSinceEpoch, partsStatus, log); } void IInterpolator::setInterpolatorSetup(const CInterpolationAndRenderingSetup &setup) @@ -143,30 +145,47 @@ namespace BlackMisc CWorker *worker = CWorker::fromTask(this, "WriteInterpolationLog", [interpolation, parts]() { - const CStatusMessage msg = IInterpolator::writeLogFile(interpolation, parts); + const CStatusMessageList msg = IInterpolator::writeLogFile(interpolation, parts); CLogMessage::preformatted(msg); }); return worker; } - CStatusMessage IInterpolator::writeLogFile(const QList &interpolation, const QList &parts) + CStatusMessageList IInterpolator::writeLogFile(const QList &interpolation, const QList &parts) { if (parts.isEmpty() && interpolation.isEmpty()) { return CStatusMessage(static_cast(nullptr)).warning("No data for log"); } - const QString htmlInterpolation = IInterpolator::getHtmlInterpolationLog(interpolation); - const QString htmlParts = IInterpolator::getHtmlPartsLog(parts); - const QString html = htmlParts % QLatin1Literal("\n\n") % htmlInterpolation; + static const QString html = QLatin1Literal("Entries: %1\n\n%2"); const QString htmlTemplate = CFileUtils::readFileToString(CBuildConfig::getHtmlTemplateFileName()); + CStatusMessageList msgs; const QString ts = QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmss"); - const QString fn = CFileUtils::appendFilePaths(CDirectoryUtils::getLogDirectory(), QString("%1 interpolation.html").arg(ts)); - const bool s = CFileUtils::writeStringToFile(htmlTemplate.arg(html), fn); - if (s) + const QString htmlInterpolation = IInterpolator::getHtmlInterpolationLog(interpolation); + if (!htmlInterpolation.isEmpty()) { - return CStatusMessage(static_cast(nullptr)).info("Written log file '%1'") << fn; + const QString fn = CFileUtils::appendFilePaths(CDirectoryUtils::getLogDirectory(), QString("%1 interpolation.html").arg(ts)); + const bool s = CFileUtils::writeStringToFile(htmlTemplate.arg(html.arg(interpolation.size()).arg(htmlInterpolation)), fn); + msgs.push_back(IInterpolator::logStatusFileWriting(s, fn)); + } + + const QString htmlParts = IInterpolator::getHtmlPartsLog(parts); + if (!htmlParts.isEmpty()) + { + const QString fn = CFileUtils::appendFilePaths(CDirectoryUtils::getLogDirectory(), QString("%1 parts.html").arg(ts)); + const bool s = CFileUtils::writeStringToFile(htmlTemplate.arg(html.arg(parts.size()).arg(htmlParts)), fn); + msgs.push_back(IInterpolator::logStatusFileWriting(s, fn)); + } + return msgs; + } + + CStatusMessage IInterpolator::logStatusFileWriting(bool success, const QString &fileName) + { + if (success) + { + return CStatusMessage(static_cast(nullptr)).info("Written log file '%1'") << fileName; } else { - return CStatusMessage(static_cast(nullptr)).error("Failed to write log file '%1'") << fn; + return CStatusMessage(static_cast(nullptr)).error("Failed to write log file '%1'") << fileName; } } @@ -191,10 +210,9 @@ namespace BlackMisc QString IInterpolator::getHtmlInterpolationLog(const QList &logs) { if (logs.isEmpty()) { return {}; } - QString tableRows; const QString tableHeader = - QLatin1Literal("") % - QLatin1Literal("CSVTOLtimestamp") % + QLatin1Literal("") % + QLatin1Literal("c.CSVTOLtimestampsince") % QLatin1Literal("ts oldts newts cur") % QLatin1Literal("ΔtΔt fr.fraction") % QLatin1Literal("lat.oldlat.newlat.cur") % @@ -203,18 +221,30 @@ namespace BlackMisc QLatin1Literal("elv.oldelv.newelv.cur") % QLatin1Literal("gnd.factor") % QLatin1Literal("onGnd.oldonGnd.newonGnd.cur") % - QLatin1Literal("partsparts details") % - QLatin1Literal("\n"); + QLatin1Literal("partsc.parts details") % + QLatin1Literal("\n"); static const CLengthUnit ft = CLengthUnit::ft(); + const InterpolationLog firstLog = logs.first(); + qint64 newPosTs = firstLog.newSituation.getMSecsSinceEpoch(); + CAircraftParts lastParts; // default, so shown if parts are different from default + + QString tableRows("\n"); for (const InterpolationLog &log : logs) { + const bool changedNewPosition = newPosTs != log.newSituation.getMSecsSinceEpoch(); + const bool changedParts = lastParts != log.parts; + newPosTs = log.newSituation.getMSecsSinceEpoch(); + lastParts = log.parts; + // concatenating in multiple steps, otherwise C4503 warnings tableRows += QLatin1Literal("") % + (changedNewPosition ? QLatin1Literal("*") : QLatin1Literal("")) % QLatin1Literal("") % log.callsign.asString() % QLatin1Literal("") % QLatin1Literal("") % boolToYesNo(log.vtolAircraft) % QLatin1Literal("") % QLatin1Literal("") % msSinceEpochToTime(log.timestamp) % QLatin1Literal("") % + QLatin1Literal("") % QString::number(log.timestamp - firstLog.timestamp) % QLatin1Literal("") % QLatin1Literal("") % msSinceEpochToTime(log.oldSituation.getAdjustedMSecsSinceEpoch()) % QLatin1Char('-') % QString::number(log.oldSituation.getTimeOffsetMs()) % QLatin1Literal("") % QLatin1Literal("") % msSinceEpochToTime(log.newSituation.getAdjustedMSecsSinceEpoch()) % QLatin1Char('-') % QString::number(log.newSituation.getTimeOffsetMs()) % QLatin1Literal("") % @@ -249,33 +279,38 @@ namespace BlackMisc tableRows += QLatin1Literal("") % boolToYesNo(log.useParts) % QLatin1Literal("") % + (changedParts ? QLatin1String("*") : QLatin1String("")) % QLatin1Literal("") % (log.useParts ? log.parts.toQString(true) : QLatin1Literal("")) % QLatin1Literal("") % QLatin1Literal("\n"); } - + tableRows += QLatin1Literal("\n"); return QLatin1Literal("\n") % tableHeader % tableRows % QLatin1Literal("
\n"); } QString IInterpolator::getHtmlPartsLog(const QList &logs) { if (logs.isEmpty()) { return {}; } - QString tableRows; const QString tableHeader = - QLatin1Literal("") % + QLatin1Literal("") % QLatin1Literal("CStimestamp") % + QLatin1Literal("c.") % QLatin1Literal("parts") % - QLatin1Literal("\n"); + QLatin1Literal("\n"); + CAircraftParts lastParts; // default, so shown if parts are different from default + QString tableRows("\n"); for (const PartsLog &log : logs) { - // concatenating in multiple steps, otherwise C4503 warnings + const bool changedParts = lastParts != log.parts; + lastParts = log.parts; tableRows += QLatin1Literal("") % QLatin1Literal("") % log.callsign.asString() % QLatin1Literal("") % QLatin1Literal("") % msSinceEpochToTime(log.timestamp) % QLatin1Literal("") % + (changedParts ? QLatin1String("*") : QLatin1String("")) % QLatin1Literal("") % log.parts.toQString() % QLatin1Literal(""); } - + tableRows += QLatin1Literal("\n"); return QLatin1Literal("\n") % tableHeader % tableRows % QLatin1Literal("
\n"); } diff --git a/src/blackmisc/simulation/interpolator.h b/src/blackmisc/simulation/interpolator.h index c038dcbcb..551d0c36c 100644 --- a/src/blackmisc/simulation/interpolator.h +++ b/src/blackmisc/simulation/interpolator.h @@ -111,13 +111,13 @@ namespace BlackMisc virtual BlackMisc::Aviation::CAircraftParts getInterpolatedParts( const Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftPartsList &parts, qint64 cutoffTime, - PartsStatus &partsStatus) const; + PartsStatus &partsStatus, bool log = false) const; //! Parts before given offset time (aka pending parts) //! \threadsafe virtual BlackMisc::Aviation::CAircraftParts getInterpolatedParts( const BlackMisc::Aviation::CCallsign &callsign, qint64 cutoffTime, - PartsStatus &partsStatus) const; + PartsStatus &partsStatus, bool log = false) const; //! Enable debug messages etc. //! \threadsafe @@ -129,6 +129,11 @@ namespace BlackMisc //! Clear log file void clearLog(); + //! Enable log messages etc. + //! \threadsafe + //! \remark public for FS9 to get setup in Fs9Client + CInterpolationAndRenderingSetup getInterpolatorSetup() const; + /*! * Takes input between 0 and 1 and returns output between 0 and 1 smoothed with an S-shaped curve. * @@ -170,16 +175,12 @@ namespace BlackMisc //! Constructor IInterpolator(BlackMisc::Simulation::IRemoteAircraftProvider *provider, const QString &objectName, QObject *parent); - //! Enable debug messages etc. - //! \threadsafe - CInterpolationAndRenderingSetup getInterpolatorSetup() const; - - //! Log interpolation, only stores in memory, for performance reasons + //! Log current interpolation cycle, only stores in memory, for performance reasons //! \remark const to allow const interpolator functions //! \threadsafe void logInterpolation(const InterpolationLog &log) const; - //! Log parts, only stores in memory, for performance reasons + //! Log current parts cycle, only stores in memory, for performance reasons //! \remark const to allow const interpolator functions //! \threadsafe void logParts(const PartsLog &parts) const; @@ -202,7 +203,10 @@ namespace BlackMisc private: //! Write log to file - static CStatusMessage writeLogFile(const QList &interpolation, const QList &parts); + static CStatusMessageList writeLogFile(const QList &interpolation, const QList &parts); + + //! Status of file operation + static CStatusMessage logStatusFileWriting(bool success, const QString &fileName); //! Create readable time static QString msSinceEpochToTime(qint64 ms); diff --git a/src/blackmisc/simulation/interpolatorlinear.cpp b/src/blackmisc/simulation/interpolatorlinear.cpp index ef1f0cc25..b7b0cbd32 100644 --- a/src/blackmisc/simulation/interpolatorlinear.cpp +++ b/src/blackmisc/simulation/interpolatorlinear.cpp @@ -67,7 +67,6 @@ namespace BlackMisc // interpolation situations CAircraftSituation oldSituation; CAircraftSituation newSituation; - const bool logInterpolation = setup.getLogCallsigns().contains(callsign); InterpolationLog log; // latest first, now 00:20 split time @@ -215,7 +214,7 @@ namespace BlackMisc status.setChangedPosition(true); } status.setInterpolationSucceeded(true); - if (logInterpolation) + if (hints.isLoggingInterpolation()) { log.timestamp = currentTimeMsSinceEpoc; log.callsign = callsign; @@ -224,6 +223,7 @@ namespace BlackMisc log.oldSituation = oldSituation; log.newSituation = newSituation; log.useParts = hints.hasAircraftParts(); + log.parts = hints.getAircraftParts(); this->logInterpolation(log); } diff --git a/src/blackmisc/simulation/remoteaircraftprovider.cpp b/src/blackmisc/simulation/remoteaircraftprovider.cpp index 86abeb112..6f5d6c719 100644 --- a/src/blackmisc/simulation/remoteaircraftprovider.cpp +++ b/src/blackmisc/simulation/remoteaircraftprovider.cpp @@ -120,6 +120,7 @@ namespace BlackMisc void IRemoteAircraftProvider::removeOutdatedParts(CAircraftPartsList &partsList) { + // remove outdated parts (but never remove the most recent one) const auto predicate = [now = partsList.front().getMSecsSinceEpoch()](const auto & p) { return p.getMSecsSinceEpoch() >= now - PartsPerCallsignMaxAgeInSeconds * 1000; }; const auto newEnd = std::find_if(partsList.rbegin(), partsList.rend(), predicate).base(); partsList.erase(newEnd, partsList.end()); diff --git a/src/blackmisc/simulation/remoteaircraftprovider.h b/src/blackmisc/simulation/remoteaircraftprovider.h index faef2450c..4b35ac635 100644 --- a/src/blackmisc/simulation/remoteaircraftprovider.h +++ b/src/blackmisc/simulation/remoteaircraftprovider.h @@ -166,7 +166,7 @@ namespace BlackMisc std::function aircraftSnapshot ) = 0; - //! Remove outdated aircraft parts + //! Remove outdated aircraft parts, but never the most recent one void static removeOutdatedParts(Aviation::CAircraftPartsList &partsList); }; diff --git a/src/plugins/simulator/fs9/fs9client.cpp b/src/plugins/simulator/fs9/fs9client.cpp index c8d23f0e3..7fd4b17bc 100644 --- a/src/plugins/simulator/fs9/fs9client.cpp +++ b/src/plugins/simulator/fs9/fs9client.cpp @@ -170,7 +170,8 @@ namespace BlackSimPlugin if (m_clientStatus == Disconnected) { return; } IInterpolator::InterpolationStatus status; - const CInterpolationHints hints; // \fixme 201701 #865 KB if there is an elevation provider for FS9 add it here or set elevation + CInterpolationHints hints; // \fixme 201701 #865 KB if there is an elevation provider for FS9 add it here or set elevation + hints.setLoggingInterpolation(this->m_interpolator->getInterpolatorSetup().getLogCallsigns().contains(m_callsign)); const CAircraftSituation situation = this->m_interpolator->getInterpolatedSituation(m_callsign, -1, hints, status); // Test only for successful interpolation. FS9 requires constant positions diff --git a/src/plugins/simulator/fsx/simulatorfsx.cpp b/src/plugins/simulator/fsx/simulatorfsx.cpp index 653bff27e..77406540b 100644 --- a/src/plugins/simulator/fsx/simulatorfsx.cpp +++ b/src/plugins/simulator/fsx/simulatorfsx.cpp @@ -863,6 +863,9 @@ namespace BlackSimPlugin // values used for position and parts const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch(); const QList simObjects(m_simConnectObjects.values()); + const CCallsignSet callsignsToLog(this->m_interpolationRenderingSetup.getLogCallsigns()); + + // interpolation for all remote aircraft for (const CSimConnectObject &simObj : simObjects) { // happening if aircraft is not yet added to simulator or to be deleted @@ -875,14 +878,17 @@ namespace BlackSimPlugin // fetch parts, as they are needed for ground interpolation const bool useAircraftParts = enableAircraftParts && aircraftWithParts.contains(callsign); + const bool logInterpolationAndParts = callsignsToLog.contains(callsign); IInterpolator::PartsStatus partsStatus; partsStatus.setSupportsParts(useAircraftParts); - const CAircraftParts parts = useAircraftParts ? this->m_interpolator->getInterpolatedParts(callsign, -1, partsStatus) : CAircraftParts(); + const CAircraftParts parts = useAircraftParts ? this->m_interpolator->getInterpolatedParts(callsign, -1, partsStatus, logInterpolationAndParts) : CAircraftParts(); // get interpolated situation IInterpolator::InterpolationStatus interpolatorStatus; CInterpolationHints hints(m_hints[simObj.getCallsign()]); hints.setAircraftParts(useAircraftParts ? parts : CAircraftParts(), useAircraftParts); + hints.setLoggingInterpolation(logInterpolationAndParts); + const CAircraftSituation interpolatedSituation = this->m_interpolator->getInterpolatedSituation(callsign, currentTimestamp, hints, interpolatorStatus); if (interpolatorStatus.allTrue())