From 41685df4eed8c6ee817f8795bc7b869a94d3081f Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 31 Oct 2018 15:27:02 +0100 Subject: [PATCH] Ref T421, interpolation log messages in interpolator(s) --- src/blackmisc/simulation/interpolator.cpp | 115 +++++++++++++----- src/blackmisc/simulation/interpolator.h | 15 +++ .../simulation/interpolatormulti.cpp | 12 ++ src/blackmisc/simulation/interpolatormulti.h | 4 + .../simulation/interpolatorspline.cpp | 21 ++-- .../fsxcommon/simulatorfsxcommon.cpp | 11 +- 6 files changed, 141 insertions(+), 37 deletions(-) diff --git a/src/blackmisc/simulation/interpolator.cpp b/src/blackmisc/simulation/interpolator.cpp index e61a5618f..a16024b0c 100644 --- a/src/blackmisc/simulation/interpolator.cpp +++ b/src/blackmisc/simulation/interpolator.cpp @@ -179,13 +179,15 @@ namespace BlackMisc // CInterpolatorLinear::Interpolant or CInterpolatorSpline::Interpolant SituationLog log; const auto interpolant = derived()->getInterpolant(log); - const bool isValid = interpolant.isValid(); + const bool isValidInterpolant = interpolant.isValid(); CAircraftSituation currentSituation = m_lastSituation; CAircraftSituation::AltitudeCorrection altCorrection = CAircraftSituation::NoCorrection; - if (isValid) + bool isValidInterpolation = false; + do { + if (!isValidInterpolant) { break; } const CInterpolatorPbh pbh = interpolant.pbh(); // init interpolated situation @@ -200,6 +202,8 @@ namespace BlackMisc // use derived interpolant function const bool interpolateGndFlag = pbh.getNewSituation().hasGroundDetailsForGndInterpolation() && pbh.getOldSituation().hasGroundDetailsForGndInterpolation(); currentSituation = interpolant.interpolatePositionAndAltitude(currentSituation, interpolateGndFlag); + if (currentSituation.isNull()) { break; } + if (CBuildConfig::isLocalDeveloperDebugBuild()) { Q_ASSERT_X(currentSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid interpolation situation"); @@ -235,35 +239,62 @@ namespace BlackMisc currentSituation.setPitch(correctedPitchOnGround); } } + + isValidInterpolation = true; } - else + while (false); + + const bool valid = isValidInterpolant && isValidInterpolation; + if (!valid) { + // further handling could go here, mainly we continue with last situation m_invalidSituations++; - // further handling could go here, mainly we continue with last situation - const bool noSituation = currentSituation.isNull(); - const qint64 diff = noSituation ? -1 : m_currentTimeMsSinceEpoch - currentSituation.getAdjustedMSecsSinceEpoch(); - const qint64 thresholdMs = noSituation ? qRound(CFsdSetup::c_interimPositionTimeOffsetMsec * 0.5) : qRound(currentSituation.getTimeOffsetMs() * 0.5); - const bool threshold = diff > thresholdMs; - if (noSituation || threshold) + // avoid flooding of log. + if (m_currentTimeMsSinceEpoch - m_lastInvalidLogTs > m_lastSituation.getTimeOffsetMs()) { + m_lastInvalidLogTs = m_currentTimeMsSinceEpoch; + const bool noSituation = m_lastSituation.isNull(); + // Problem 1, we have no "last situation" // Problem 2, "it takes too long to recover" + CStatusMessage m; if (noSituation) { - CLogMessage(this).warning("No situation no %1 for interpolation reported for '%2'") << m_invalidSituations << m_callsign.asString(); + m = CStatusMessage(this).warning("No situation #%1 for interpolation reported for '%2' (Interpolant: %3 interpolation: %4)") << + m_invalidSituations << m_callsign.asString() << boolToTrueFalse(isValidInterpolant) << boolToTrueFalse(isValidInterpolation); } else { - CLogMessage(this).warning("Invalid situation, diff %1ms no %2 for interpolation reported for '%3'") << diff << m_invalidSituations << m_callsign.asString(); + const qint64 diff = noSituation ? -1 : m_currentTimeMsSinceEpoch - currentSituation.getAdjustedMSecsSinceEpoch(); + m = CStatusMessage(this).warning("Invalid situation, diff. %1ms #%2 for interpolation reported for '%3' (Interpolant: %4 interpolation: %5)") << + diff << m_invalidSituations << m_callsign.asString() << boolToTrueFalse(isValidInterpolant) << boolToTrueFalse(isValidInterpolation); + } + if (!m.isEmpty()) + { + if (m_interpolationMessages.isEmpty()) + { + // display first message as a hint in the general log + CLogMessage::preformatted(m); + } + m_interpolationMessages.push_back(m); } } }// valid? - // status - Q_ASSERT_X(currentSituation.hasMSLGeodeticHeight(), Q_FUNC_INFO, "No MSL altitude"); - m_currentInterpolationStatus.setInterpolatedAndCheckSituation(isValid, currentSituation); - m_lastSituation = currentSituation; + // situation and status + if (valid) + { + Q_ASSERT_X(currentSituation.hasMSLGeodeticHeight(), Q_FUNC_INFO, "No MSL altitude"); + m_lastSituation = currentSituation; + m_currentInterpolationStatus.setInterpolatedAndCheckSituation(valid, currentSituation); + } + else + { + currentSituation = m_lastSituation; + m_currentInterpolationStatus.setSameSituation(true); + m_currentInterpolationStatus.setInterpolatedAndCheckSituation(valid, currentSituation); + } // logging if (this->doLogging()) @@ -332,33 +363,36 @@ namespace BlackMisc if (!doGuess && !doInterpolation) { - m_currentPartsStatus = m_lastPartsStatus; - m_currentPartsStatus.setReusedParts(true); - return m_lastParts; + // reuse + return this->logAndReturnNullParts("neither guess nor interpolation", true); } - CAircraftParts parts; + CAircraftParts parts = CAircraftParts::null(); if (m_currentSetup.isAircraftPartsEnabled()) { - // this already logs + // this already logs and sets status parts = this->getInterpolatedParts(); } - // if we have supported parts, we skip this step, but it can happen - // the parts are still empty + // if we have supported parts, we skip this step, but it can happen the parts are still empty if (!m_currentPartsStatus.isSupportingParts()) { if (!doGuess) { - m_currentPartsStatus = m_lastPartsStatus; - m_currentPartsStatus.setReusedParts(true); - return m_lastParts; + return this->logAndReturnNullParts("not supporting parts, and marked for guessing", true); } // check if model has been thru model matching - Q_ASSERT_X(!m_lastSituation.isNull(), Q_FUNC_INFO, "null situations"); - parts.guessParts(m_lastSituation, m_pastSituationsChange, m_model); - this->logParts(parts, 0, false); + if (!m_lastSituation.isNull()) + { + parts.guessParts(m_lastSituation, m_pastSituationsChange, m_model); + this->logParts(parts, 0, false); + } + else + { + // quite normal initial situation, just return NULL + return this->logAndReturnNullParts("guessing, but no situation yet", false); + } } m_lastParts = parts; @@ -366,6 +400,27 @@ namespace BlackMisc return parts; } + template + const CAircraftParts &CInterpolator::logAndReturnNullParts(const QString &info, bool log) + { + if (!m_lastParts.isNull()) + { + m_currentPartsStatus = m_lastPartsStatus; + m_currentPartsStatus.setReusedParts(true); + return m_lastParts; + } + + + if (log) + { + const CStatusMessage m = CStatusMessage(this).warning("NULL parts reported for '%1', '%2')") << m_callsign.asString() << info; + if (m_interpolationMessages.isEmpty()) { CLogMessage::preformatted(m); } + m_interpolationMessages.push_back(m); + } + m_currentPartsStatus.reset(); + return CAircraftParts::null(); + } + template bool CInterpolator::doLogging() const { @@ -418,6 +473,9 @@ namespace BlackMisc m_currentInterpolationStatus.reset(); m_currentPartsStatus.reset(); m_interpolatedSituationsCounter = 0; + m_invalidSituations = 0; + m_lastInvalidLogTs = -1; + m_interpolationMessages.clear(); } template @@ -564,6 +622,7 @@ namespace BlackMisc void CInterpolationStatus::checkIfValidSituation(const CAircraftSituation &situation) { m_isValidSituation = !situation.isPositionOrAltitudeNull(); + if (!m_isValidSituation) { m_isValidSituation = false; } } bool CInterpolationStatus::hasValidInterpolatedSituation() const diff --git a/src/blackmisc/simulation/interpolator.h b/src/blackmisc/simulation/interpolator.h index b17aaf7f3..3bf69dd36 100644 --- a/src/blackmisc/simulation/interpolator.h +++ b/src/blackmisc/simulation/interpolator.h @@ -22,6 +22,7 @@ #include "blackmisc/aviation/aircraftpartslist.h" #include "blackmisc/aviation/callsign.h" #include "blackmisc/logcategorylist.h" +#include "blackmisc/statusmessagelist.h" #include #include @@ -233,6 +234,12 @@ namespace BlackMisc //! Get count of invalid situations int getInvalidSituationsCount() const { return m_invalidSituations; } + //! Interpolation messages + const CStatusMessageList &getInterpolationMessages() const { return m_interpolationMessages; } + + //! Do we have interpolation messages + bool hasInterpolationMessages() const { return !m_interpolationMessages.isEmpty(); } + protected: //! Constructor CInterpolator(const Aviation::CCallsign &callsign, @@ -281,6 +288,9 @@ namespace BlackMisc int m_partsToSituationInterpolationRatio = 2; //!< ratio between parts and situation interpolation, 1..always, 2..every 2nd situation int m_partsToSituationGuessingRatio = 5; //!< ratio between parts guessing and situation interpolation int m_invalidSituations = 0; //!< mainly when there are no new situations + qint64 m_lastInvalidLogTs = -1; //!< last invalid situation timestamp + CStatusMessageList m_interpolationMessages; //!< interpolation messages + Aviation::CAircraftSituation m_lastSituation { Aviation::CAircraftSituation::null() }; //!< latest interpolation Aviation::CAircraftParts m_lastParts { Aviation::CAircraftParts::null() }; //!< latest parts PhysicalQuantities::CLength m_currentSceneryOffset { PhysicalQuantities::CLength::null() }; //!< calculated scenery offset if any @@ -312,8 +322,13 @@ namespace BlackMisc //! Deferred init void deferredInit(); + //! Return NULL parts and log + const BlackMisc::Aviation::CAircraftParts &logAndReturnNullParts(const QString &info, bool log); + + //! Derived class @{ Derived *derived() { return static_cast(this); } const Derived *derived() const { return static_cast(this); } + //! @} }; //! \cond PRIVATE diff --git a/src/blackmisc/simulation/interpolatormulti.cpp b/src/blackmisc/simulation/interpolatormulti.cpp index 76dc105cd..56e3a9d49 100644 --- a/src/blackmisc/simulation/interpolatormulti.cpp +++ b/src/blackmisc/simulation/interpolatormulti.cpp @@ -57,6 +57,18 @@ namespace BlackMisc m_spline.initCorrespondingModel(model); } + const CStatusMessageList &CInterpolatorMulti::getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + switch (mode) + { + case CInterpolationAndRenderingSetupBase::Spline: return m_spline.getInterpolationMessages(); + case CInterpolationAndRenderingSetupBase::Linear: return m_linear.getInterpolationMessages(); + default: break; + } + static const CStatusMessageList empty; + return empty; + } + QString CInterpolatorMulti::getInterpolatorInfo(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const { switch (mode) diff --git a/src/blackmisc/simulation/interpolatormulti.h b/src/blackmisc/simulation/interpolatormulti.h index baf8a5c6a..997f26e51 100644 --- a/src/blackmisc/simulation/interpolatormulti.h +++ b/src/blackmisc/simulation/interpolatormulti.h @@ -14,6 +14,7 @@ #include "blackmisc/simulation/interpolatorlinear.h" #include "blackmisc/simulation/interpolatorspline.h" +#include "blackmisc/statusmessagelist.h" namespace BlackMisc { @@ -34,6 +35,9 @@ namespace BlackMisc //! \copydoc CInterpolator::getLastInterpolatedSituation const Aviation::CAircraftSituation &getLastInterpolatedSituation(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + //! \copydoc CInterpolator::getInterpolationMessages + const CStatusMessageList &getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + //! \copydoc CInterpolator::attachLogger void attachLogger(CInterpolationLogger *logger); diff --git a/src/blackmisc/simulation/interpolatorspline.cpp b/src/blackmisc/simulation/interpolatorspline.cpp index e632af93e..3498bd377 100644 --- a/src/blackmisc/simulation/interpolatorspline.cpp +++ b/src/blackmisc/simulation/interpolatorspline.cpp @@ -312,31 +312,38 @@ namespace BlackMisc const double t1 = m_pa.t[1]; const double t2 = m_pa.t[2]; // latest (adjusted) - if (CBuildConfig::isLocalDeveloperDebugBuild()) + bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2); + if (!valid && CBuildConfig::isLocalDeveloperDebugBuild()) { - Q_ASSERT_X(t1 < t2, Q_FUNC_INFO, "Expect sorted times, latest first"); - Q_ASSERT_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO, "invalid timestamp t1"); - Q_ASSERT_X(m_currentTimeMsSinceEpoc < t2, Q_FUNC_INFO, "invalid timestamp t2"); // t1==t2 results in div/0 + Q_ASSERT_X(t1 < t2, Q_FUNC_INFO, "Expect sorted times, latest first"); // that means a bug in our code init the values + BLACK_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO, "invalid timestamp t1"); + BLACK_VERIFY_X(m_currentTimeMsSinceEpoc < t2, Q_FUNC_INFO, "invalid timestamp t2"); // t1==t2 results in div/0 } + if (!valid) { return CAircraftSituation::null(); } const double newX = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]); const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]); const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]); - if (CBuildConfig::isLocalDeveloperDebugBuild()) + valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) && CAircraftSituation::isValidVector(m_pa.z); + if (!valid && CBuildConfig::isLocalDeveloperDebugBuild()) { BLACK_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO, "invalid X"); // all x values BLACK_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO, "invalid Y"); // all y values BLACK_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO, "invalid Z"); // all z values } + if (!valid) { return CAircraftSituation::null(); } CAircraftSituation newSituation(currentSituation); const std::array normalVector = {{ newX, newY, newZ }}; const CCoordinateGeodetic currentPosition(normalVector); - if (CBuildConfig::isLocalDeveloperDebugBuild()) + + valid = CAircraftSituation::isValidVector(normalVector); + if (!valid && CBuildConfig::isLocalDeveloperDebugBuild()) { - BLACK_VERIFY_X(CAircraftSituation::isValidVector(normalVector), Q_FUNC_INFO, "invalid vector"); + BLACK_VERIFY_X(valid, Q_FUNC_INFO, "invalid vector"); } + if (!valid) { return CAircraftSituation::null(); } const double newA = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]); const CAltitude alt(newA, m_altitudeUnit); diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp index a3b58a0e6..9b0ff729e 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp @@ -1737,8 +1737,9 @@ namespace BlackSimPlugin { SIMCONNECT_DATA_INITPOSITION position = this->aircraftSituationToFsxPosition(result, sendGround); const HRESULT hr = this->logAndTraceSendId( - SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition, - static_cast(objectId), 0, 0, sizeof(SIMCONNECT_DATA_INITPOSITION), &position), + SimConnect_SetDataOnSimObject( + m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition, + static_cast(objectId), 0, 0, sizeof(SIMCONNECT_DATA_INITPOSITION), &position), traceSendId, simObject, "Failed to set position", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject"); if (isOk(hr)) { @@ -1749,10 +1750,15 @@ namespace BlackSimPlugin } else { + // already logged in interpolator + continue; + + /** static const QString so("SimObject id: %1"); const QString msg = this->getInvalidSituationLogMessage(callsign, result.getInterpolationStatus(), so.arg(objectId)); const CStatusMessage sm(this, CStatusMessage::SeverityWarning, msg); this->clampedLog(callsign, sm); + **/ } // Interpolated parts @@ -1769,6 +1775,7 @@ namespace BlackSimPlugin if (!simObject.hasValidRequestAndObjectId()) { return false; } const CAircraftParts parts = result; + if (parts.isNull()) { return false; } if (parts.getPartsDetails() != CAircraftParts::GuessedParts && !result.getPartsStatus().isSupportingParts()) { return false; } if (result.getPartsStatus().isReusedParts() || this->isEqualLastSent(parts, simObject.getCallsign())) { return true; }