diff --git a/src/blackcore/simulatorcommon.cpp b/src/blackcore/simulatorcommon.cpp index a9d5ca98a..6499a6e36 100644 --- a/src/blackcore/simulatorcommon.cpp +++ b/src/blackcore/simulatorcommon.cpp @@ -104,7 +104,7 @@ namespace BlackCore Q_ASSERT_X(remoteAircraft.hasModelString(), Q_FUNC_INFO, "Missing model string"); Q_ASSERT_X(remoteAircraft.hasCallsign(), Q_FUNC_INFO, "Missing callsign"); - const bool renderingRestricted = m_interpolationRenderingSetup.isRenderingRestricted(); + const bool renderingRestricted = this->getInterpolationAndRenderingSetup().isRenderingRestricted(); if (this->showDebugLogMessage()) { this->debugLogMessage(Q_FUNC_INFO, QString("Restricted: %1 cs: '%2' enabled: %3").arg(boolToYesNo(renderingRestricted), remoteAircraft.getCallsignAsString(), boolToYesNo(remoteAircraft.isEnabled()))); } if (!remoteAircraft.isEnabled()) { return false; } @@ -118,7 +118,7 @@ namespace BlackCore bool CSimulatorCommon::logicallyRemoveRemoteAircraft(const CCallsign &callsign) { // if not restriced, directly change - if (!m_interpolationRenderingSetup.isRenderingRestricted()) + if (!this->getInterpolationAndRenderingSetup().isRenderingRestricted()) { m_statsPhysicallyAddedAircraft++; this->callPhysicallyRemoveRemoteAircraft(callsign); return true; @@ -224,7 +224,7 @@ namespace BlackCore bool CSimulatorCommon::showDebugLogMessage() const { - return m_interpolationRenderingSetup.showSimulatorDebugMessages(); + return this->getInterpolationAndRenderingSetup().showSimulatorDebugMessages(); } void CSimulatorCommon::reverseLookupAndUpdateOwnAircraftModel(const BlackMisc::Simulation::CAircraftModel &model) @@ -452,6 +452,8 @@ namespace BlackCore { m_interpolationLogger.clearLog(); CStatusMessage(this).info("Cleared interpolation logging"); + QWriteLocker l(&m_interpolationRenderingSetupMutex); + m_interpolationRenderingSetup.clearInterpolatorLogCallsigns(); return true; } if (part2.startsWith("max")) @@ -492,18 +494,18 @@ namespace BlackCore return true; } - const QString cs = part2.toUpper(); - if (!CCallsign::isValidAircraftCallsign(cs)) { return false; } + const CCallsign cs(part2.toUpper()); + if (!cs.isValid()) { return false; } if (this->getAircraftInRangeCallsigns().contains(cs)) { - CLogMessage(this).info("Will log interpolation for '%1'") << cs; + CLogMessage(this).info("Will log interpolation for '%1'") << cs.asString(); QWriteLocker l(&m_interpolationRenderingSetupMutex); - m_interpolationRenderingSetup.addCallsignToLog(CCallsign(cs)); + m_interpolationRenderingSetup.addCallsignToLog(cs); return true; } else { - CLogMessage(this).warning("Cannot log interpolation for '%1', no aircraft in range") << cs; + CLogMessage(this).warning("Cannot log interpolation for '%1', no aircraft in range") << cs.asString(); return false; } } // logint @@ -520,18 +522,31 @@ namespace BlackCore if (part1.startsWith("pos")) { - const QString cs = parser.part(2).toUpper(); - if (!CCallsign::isValidAircraftCallsign(cs)) { return false; } - if (this->getAircraftInRangeCallsigns().contains(cs) && m_interpolationRenderingSetup.getLogCallsigns().containsCallsign(cs)) + CCallsign cs(parser.part(2).toUpper()); + const CInterpolationAndRenderingSetup s = this->getInterpolationAndRenderingSetup(); + if (!cs.isValid()) { - CAircraftSituation s = m_interpolationLogger.getLastSituation(cs); - if (!s.getPosition().isNull()) - { - this->displayStatusMessage(CStatusMessage(this).info(s.toQString(true))); - } - return true; + + if (s.getLogCallsigns().size() != 1) { return false; } + + // if there is just one we take that one + cs = s.getLogCallsigns().toQList().front(); } - return false; + + bool addedCallsign = false; + { + const bool isLoggedCallsign = s.isLogCallsign(cs); + QWriteLocker l(&m_interpolationRenderingSetupMutex); + if (!isLoggedCallsign) + { + m_interpolationRenderingSetup.addCallsignToLog(cs); + addedCallsign = true; + } + } + + CLogMessage(this).info("Display position for '%1'") << cs.asString(); + this->displayLoggedSituationInSimulator(cs, addedCallsign); + return true; } if (parser.hasPart(2) && (part1.startsWith("aircraft") || part1.startsWith("ac"))) @@ -582,11 +597,11 @@ namespace BlackCore CSimpleCommandParser::registerCommand({".drv logint off", "no log information for interpolator"}); CSimpleCommandParser::registerCommand({".drv logint write", "write interpolator log to file"}); CSimpleCommandParser::registerCommand({".drv logint clear", "clear current log"}); - CSimpleCommandParser::registerCommand({".drv logint max number", "max.number of entries logged"}); + CSimpleCommandParser::registerCommand({".drv logint max number", "max. number of entries logged"}); CSimpleCommandParser::registerCommand({".drv pos callsign", "show position for callsign"}); CSimpleCommandParser::registerCommand({".drv spline|linear callsign", "set spline/linear interpolator for one/all callsign(s)"}); - CSimpleCommandParser::registerCommand({".drv aircraft readd callsign", "add again a given callsign"}); - CSimpleCommandParser::registerCommand({".drv aircraft readd all", "add again all aircraft"}); + CSimpleCommandParser::registerCommand({".drv aircraft readd callsign", "add again (re-add) a given callsign"}); + CSimpleCommandParser::registerCommand({".drv aircraft readd all", "add again (re-add) all aircraft"}); CSimpleCommandParser::registerCommand({".drv aircraft rm callsign", "remove a given callsign"}); } @@ -769,4 +784,41 @@ namespace BlackCore m_statsPhysicallyRemovedAircraft++; this->physicallyRemoveRemoteAircraft(remoteCallsign); } + + void CSimulatorCommon::displayLoggedSituationInSimulator(const CCallsign &cs, bool stopLogging, int times) + { + if (cs.isEmpty()) { return; } + if (this->isShuttingDown()) { return; } + const CInterpolationAndRenderingSetup setup = this->getInterpolationAndRenderingSetup(); + const bool logsCs = setup.isLogCallsign(cs); + if (!logsCs) { return; } + + stopLogging = stopLogging || !this->isSimulating(); // stop when sim was stopped + stopLogging = stopLogging && logsCs; + if (!stopLogging && times < 1) { return; } + + const bool inRange = this->getAircraftInRangeCallsigns().contains(cs); + if (!stopLogging && !inRange) { return; } + if (stopLogging && (times < 1 || !inRange)) + { + QWriteLocker wl(&m_interpolationRenderingSetupMutex); + m_interpolationRenderingSetup.removeCallsignFromLog(cs); + return; + } + + const CInterpolationLogger::SituationLog s = m_interpolationLogger.getLastSituationLog(cs); + const CInterpolationLogger::PartsLog p = m_interpolationLogger.getLastPartsLog(cs); + + QString dm; + static const QString sep("\n"); + if (s.tsCurrent > 0) { dm = QStringLiteral("Situation: ") % s.toQString(true, true, true, true, true, true, sep); } + if (p.tsCurrent > 0) { dm += (dm.isEmpty() ? QStringLiteral("") : "\n\n") % QStringLiteral("Parts: ") % p.toQString(sep); } + if (!dm.isEmpty()) { this->displayStatusMessage(CStatusMessage(this).info(dm)); } + + const int t = 4500 + (qrand() % 1000); // makes sure not always using the same time difference + QTimer::singleShot(t, this, [ = ] + { + this->displayLoggedSituationInSimulator(cs, stopLogging, times - 1); + }); + } } // namespace diff --git a/src/blackcore/simulatorcommon.h b/src/blackcore/simulatorcommon.h index 5d9469d90..c6c02d560 100644 --- a/src/blackcore/simulatorcommon.h +++ b/src/blackcore/simulatorcommon.h @@ -231,15 +231,16 @@ namespace BlackCore BlackMisc::Simulation::CAircraftModel m_defaultModel; //!< default model BlackMisc::Simulation::CSimulatorInternals m_simulatorInternals; //!< setup object BlackMisc::Simulation::CInterpolationLogger m_interpolationLogger; //!< log interpolation - mutable QReadWriteLock m_interpolationRenderingSetupMutex; //!< mutex protecting setup object - // setup for debugging, logs .. + // setup for logging etc. + mutable QReadWriteLock m_interpolationRenderingSetupMutex; //!< mutex protecting setup object BlackMisc::Simulation::CInterpolationAndRenderingSetup m_interpolationRenderingSetup; //!< logging, rendering etc. // 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 QHash m_hints; //!< hints for callsign, contains last ground elevation fetched + // 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 BlackMisc::CSetting m_weatherScenarioSettings { this, &CSimulatorCommon::reloadWeatherSettings }; //!< Selected weather scenario @@ -258,6 +259,9 @@ namespace BlackCore void callPhysicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft); void callPhysicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &remoteCallsign); + //! Display a logged situation in simulator + void displayLoggedSituationInSimulator(const BlackMisc::Aviation::CCallsign &cs, bool stopLogging, int times = 10); + bool m_blinkCycle = false; //!< used for highlighting qint64 m_highlightEndTimeMsEpoch = 0; //!< end highlighting int m_timerCounter = 0; //!< allows to calculate n seconds @@ -269,10 +273,10 @@ namespace BlackCore // statistics values of how often those functions are called // those are the added counters, overflow will not be an issue here (discussed in T171 review) - int m_statsPhysicallyAddedAircraft = 0; //!< statistics, how many aircraft added - int m_statsPhysicallyRemovedAircraft = 0; //!< statistics, how many aircraft removed - int m_statsPartsAdded = 0; //!< statistics, how many aircraft parts added - int m_statsSituationAdded = 0; //!< statistics, how many situations added + int m_statsPhysicallyAddedAircraft = 0; //!< statistics, how many aircraft added + int m_statsPhysicallyRemovedAircraft = 0; //!< statistics, how many aircraft removed + int m_statsPartsAdded = 0; //!< statistics, how many aircraft parts added + int m_statsSituationAdded = 0; //!< statistics, how many situations added }; } // namespace diff --git a/src/blackmisc/simulation/interpolationhints.cpp b/src/blackmisc/simulation/interpolationhints.cpp index 1b9729f51..1af84ed81 100644 --- a/src/blackmisc/simulation/interpolationhints.cpp +++ b/src/blackmisc/simulation/interpolationhints.cpp @@ -36,6 +36,15 @@ namespace BlackMisc return validPlane ? this->m_elevationPlane.getAltitude() : CAltitude::null(); } + CAltitude CInterpolationHints::getGroundElevation(const CAircraftSituation &situation, const CLength &validRadius, bool useProvider, bool forceProvider) const + { + const bool validPlane = m_elevationPlane.isWithinRange(situation, CLength::maxValue(validRadius, m_elevationPlane.getRadius())); + Q_ASSERT_X(!(forceProvider && !useProvider), Q_FUNC_INFO, "Invalid parameter combination"); + if (forceProvider && useProvider && m_elevationProvider) { return m_elevationProvider(situation); } + if (!validPlane && useProvider && m_elevationProvider) { return m_elevationProvider(situation); } + return validPlane ? this->m_elevationPlane.getAltitude() : CAltitude::null(); + } + void CInterpolationHints::resetElevationPlane() { m_elevationPlane = CElevationPlane(); diff --git a/src/blackmisc/simulation/interpolationhints.h b/src/blackmisc/simulation/interpolationhints.h index 85831ef6e..6b2e22b19 100644 --- a/src/blackmisc/simulation/interpolationhints.h +++ b/src/blackmisc/simulation/interpolationhints.h @@ -63,6 +63,10 @@ namespace BlackMisc //! \see setElevationPlane Aviation::CAltitude getGroundElevation(const Aviation::CAircraftSituation &situation, bool useProvider, bool forceProvider = false) const; + //! Get elevation from CInterpolationHints::getElevationProvider or CInterpolationHints::getElevation + //! \remark if validRadius is >= Geo::CElevationPlane::radius use validRadius + Aviation::CAltitude getGroundElevation(const Aviation::CAircraftSituation &situation, const PhysicalQuantities::CLength &validRadius, bool useProvider, bool forceProvider = false) const; + //! Check if elevation is within radius and can be used bool isWithinRange(const Geo::ICoordinateGeodetic &coordinate) const; diff --git a/src/blackmisc/simulation/interpolationlogger.cpp b/src/blackmisc/simulation/interpolationlogger.cpp index b230d3986..66ca6579f 100644 --- a/src/blackmisc/simulation/interpolationlogger.cpp +++ b/src/blackmisc/simulation/interpolationlogger.cpp @@ -186,18 +186,32 @@ namespace BlackMisc return logs; } + CInterpolationLogger::SituationLog CInterpolationLogger::getLastSituationLog() const + { + QReadLocker l(&m_lockSituations); + if (m_situationLogs.isEmpty()) { return SituationLog(); } + return m_situationLogs.last(); + } + + CInterpolationLogger::SituationLog CInterpolationLogger::getLastSituationLog(const CCallsign &cs) const + { + const QList copy(this->getSituationsLog(cs)); + if (copy.isEmpty()) { return SituationLog(); } + return copy.last(); + } + CAircraftSituation CInterpolationLogger::getLastSituation() const { QReadLocker l(&m_lockSituations); if (m_situationLogs.isEmpty()) { return CAircraftSituation(); } - return m_situationLogs.last().currentSituation; + return m_situationLogs.last().situationCurrent; } CAircraftSituation CInterpolationLogger::getLastSituation(const CCallsign &cs) const { const QList copy(this->getSituationsLog(cs)); if (copy.isEmpty()) { return CAircraftSituation(); } - return copy.last().currentSituation; + return copy.last().situationCurrent; } CAircraftParts CInterpolationLogger::getLastParts() const @@ -214,6 +228,20 @@ namespace BlackMisc return copy.last().parts; } + CInterpolationLogger::PartsLog CInterpolationLogger::getLastPartsLog() const + { + QReadLocker l(&m_lockParts); + if (m_partsLogs.isEmpty()) { return PartsLog(); } + return m_partsLogs.last(); + } + + CInterpolationLogger::PartsLog CInterpolationLogger::getLastPartsLog(const CCallsign &cs) const + { + const QList copy(this->getPartsLog(cs)); + if (copy.isEmpty()) { return PartsLog(); } + return copy.last(); + } + const QString &CInterpolationLogger::filePatternInterpolationLog() { static const QString p("*interpolation.html"); @@ -235,113 +263,113 @@ namespace BlackMisc QString CInterpolationLogger::getHtmlInterpolationLog(const QList &logs) { if (logs.isEmpty()) { return {}; } - const QString tableHeader = - QLatin1String("") % - QLatin1String("cs.Int") % - QLatin1String("CSVTOLtimestampsince") % - QLatin1String("ts oldts newts cur") % - QLatin1String("ΔtΔt fr.fraction") % - QLatin1String("lat.oldlat.newlat.cur") % - QLatin1String("lng.oldlng.newlng.cur") % - QLatin1String("alt.oldalt.newalt.cur") % - QLatin1String("elv.oldelv.newelv.cur") % - QLatin1String("gnd.factor") % - QLatin1String("onGnd.oldonGnd.newonGnd.cur") % - QLatin1String("CG") % - QLatin1String("partscp.parts details") % - QLatin1String("\n"); + static const QString tableHeader = + QStringLiteral("") % + QStringLiteral("cs.Int") % + QStringLiteral("CSVTOLtimestampsince") % + QStringLiteral("ts oldts newts cur") % + QStringLiteral("Interpolation ts.Sample Δtfraction") % + QStringLiteral("lat.oldlat.newlat.cur") % + QStringLiteral("lng.oldlng.newlng.cur") % + QStringLiteral("alt.oldalt.newalt.cur") % + QStringLiteral("elv.oldelv.newelv.cur") % + QStringLiteral("gnd.factor") % + QStringLiteral("onGnd.oldonGnd.newonGnd.cur") % + QStringLiteral("CG") % + QStringLiteral("partscp.parts details") % + QStringLiteral("\n"); static const CLengthUnit ft = CLengthUnit::ft(); const SituationLog firstLog = logs.first(); - qint64 newPosTs = firstLog.newSituation.getMSecsSinceEpoch(); + qint64 newPosTs = firstLog.situationNew.getMSecsSinceEpoch(); CAircraftParts lastParts; // default, so shown if parts are different from default QString tableRows("\n"); for (const SituationLog &log : logs) { - const bool changedNewPosition = newPosTs != log.newSituation.getMSecsSinceEpoch(); - const bool changedParts = lastParts != log.parts; - newPosTs = log.newSituation.getMSecsSinceEpoch(); + const bool changedNewPosition = newPosTs != log.situationNew.getMSecsSinceEpoch(); + const bool changedParts = (lastParts != log.parts); + newPosTs = log.situationNew.getMSecsSinceEpoch(); lastParts = log.parts; // concatenating in multiple steps, otherwise C4503 warnings tableRows += - QLatin1String("") % - (changedNewPosition ? QLatin1String("*") : QLatin1String("")) % - QLatin1String("") % log.interpolator % QLatin1String("") % - QLatin1String("") % log.callsign.asString() % QLatin1String("") % - QLatin1String("") % boolToYesNo(log.vtolAircraft) % QLatin1String("") % - QLatin1String("") % msSinceEpochToTime(log.timestamp) % QLatin1String("") % - QLatin1String("") % QString::number(log.timestamp - firstLog.timestamp) % QLatin1String("") % + QStringLiteral("") % + (changedNewPosition ? QStringLiteral("*") : QStringLiteral("")) % + QStringLiteral("") % log.interpolator % QStringLiteral("") % + QStringLiteral("") % log.callsign.asString() % QStringLiteral("") % + QStringLiteral("") % boolToYesNo(log.vtolAircraft) % QStringLiteral("") % + QStringLiteral("") % msSinceEpochToTime(log.tsCurrent) % QStringLiteral("") % + QStringLiteral("") % QString::number(log.tsCurrent - firstLog.tsCurrent) % QStringLiteral("") % - QLatin1String("") % msSinceEpochToTime(log.oldSituation.getAdjustedMSecsSinceEpoch()) % QLatin1Char('-') % QString::number(log.oldSituation.getTimeOffsetMs()) % QLatin1String("") % - QLatin1String("") % msSinceEpochToTime(log.newSituation.getAdjustedMSecsSinceEpoch()) % QLatin1Char('-') % QString::number(log.newSituation.getTimeOffsetMs()) % QLatin1String("") % - QLatin1String("") % msSinceEpochToTime(log.currentSituation.getAdjustedMSecsSinceEpoch()) % QLatin1Char('-') % QString::number(log.currentSituation.getTimeOffsetMs()) % QLatin1String("") % + QStringLiteral("") % log.situationOld.getTimestampAndOffset(true) % QStringLiteral("") % + QStringLiteral("") % log.situationNew.getTimestampAndOffset(true) % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.getTimestampAndOffset(true) % QStringLiteral("") % - QLatin1String("") % QString::number(log.deltaTimeMs) % QLatin1String("") % - QLatin1String("") % QString::number(log.deltaTimeFractionMs) % QLatin1String("") % - QLatin1String("") % QString::number(log.simulationTimeFraction) % QLatin1String(""); + QStringLiteral("") % msSinceEpochToTime(log.tsInterpolated) % QStringLiteral("") % + QStringLiteral("") % QString::number(log.deltaSampleTimesMs) % QStringLiteral("ms") % + QStringLiteral("") % QString::number(log.simulationTimeFraction) % QStringLiteral(""); tableRows += - QLatin1String("") % log.oldSituation.latitudeAsString() % QLatin1String("") % - QLatin1String("") % log.newSituation.latitudeAsString() % QLatin1String("") % - QLatin1String("") % log.currentSituation.latitudeAsString() % QLatin1String("") % + QStringLiteral("") % log.situationOld.latitudeAsString() % QStringLiteral("") % + QStringLiteral("") % log.situationNew.latitudeAsString() % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.latitudeAsString() % QStringLiteral("") % - QLatin1String("") % log.oldSituation.longitudeAsString() % QLatin1String("") % - QLatin1String("") % log.newSituation.longitudeAsString() % QLatin1String("") % - QLatin1String("") % log.currentSituation.longitudeAsString() % QLatin1String(""); + QStringLiteral("") % log.situationOld.longitudeAsString() % QStringLiteral("") % + QStringLiteral("") % log.situationNew.longitudeAsString() % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.longitudeAsString() % QStringLiteral(""); tableRows += - QLatin1String("") % log.oldSituation.getAltitude().valueRoundedWithUnit(ft, 1) % QLatin1String("") % - QLatin1String("") % log.newSituation.getAltitude().valueRoundedWithUnit(ft, 1) % QLatin1String("") % - QLatin1String("") % log.currentSituation.getAltitude().valueRoundedWithUnit(ft, 1) % QLatin1String("") % + QStringLiteral("") % log.situationOld.getAltitude().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % + QStringLiteral("") % log.situationNew.getAltitude().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.getAltitude().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % - QLatin1String("") % log.oldSituation.getGroundElevation().valueRoundedWithUnit(ft, 1) % QLatin1String("") % - QLatin1String("") % log.newSituation.getGroundElevation().valueRoundedWithUnit(ft, 1) % QLatin1String("") % - QLatin1String("") % log.currentSituation.getGroundElevation().valueRoundedWithUnit(ft, 1) % QLatin1String("") % + QStringLiteral("") % log.situationOld.getGroundElevation().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % + QStringLiteral("") % log.situationNew.getGroundElevation().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.getGroundElevation().valueRoundedWithUnit(ft, 1) % QStringLiteral("") % - QLatin1String("") % QString::number(log.groundFactor) % QLatin1String("") % - QLatin1String("") % log.oldSituation.getOnGroundInfo() % QLatin1String("") % - QLatin1String("") % log.newSituation.getOnGroundInfo() % QLatin1String("") % - QLatin1String("") % log.currentSituation.getOnGroundInfo() % QLatin1String(""); + QStringLiteral("") % QString::number(log.groundFactor) % QStringLiteral("") % + QStringLiteral("") % log.situationOld.getOnGroundInfo() % QStringLiteral("") % + QStringLiteral("") % log.situationNew.getOnGroundInfo() % QStringLiteral("") % + QStringLiteral("") % log.situationCurrent.getOnGroundInfo() % QStringLiteral(""); tableRows += - QLatin1String("") % log.cgAboveGround.valueRoundedWithUnit(CLengthUnit::ft(), 0) % QLatin1String("") % - QLatin1String("") % boolToYesNo(log.useParts) % QLatin1String("") % - (changedParts ? QLatin1String("*") : QLatin1String("")) % - QLatin1String("") % (log.useParts ? log.parts.toQString(true) : QLatin1String("")) % QLatin1String("") % - QLatin1String("\n"); + QStringLiteral("") % log.cgAboveGround.valueRoundedWithUnit(ft, 0) % QStringLiteral("") % + QStringLiteral("") % boolToYesNo(log.useParts) % QStringLiteral("") % + (changedParts ? QStringLiteral("*") : QStringLiteral("")) % + QStringLiteral("") % (log.useParts ? log.parts.toQString(true) : QStringLiteral("")) % QStringLiteral("") % + QStringLiteral("\n"); } - tableRows += QLatin1String("\n"); - return QLatin1String("\n") % tableHeader % tableRows % QLatin1String("
\n"); + tableRows += QStringLiteral("\n"); + return QStringLiteral("\n") % tableHeader % tableRows % QStringLiteral("
\n"); } QString CInterpolationLogger::getHtmlPartsLog(const QList &logs) { if (logs.isEmpty()) { return {}; } - const QString tableHeader = - QLatin1String("") % - QLatin1String("CStimestamp") % - QLatin1String("c.") % - QLatin1String("parts") % - QLatin1String("\n"); + static const QString tableHeader = + QStringLiteral("") % + QStringLiteral("CStimestamp") % + QStringLiteral("c.") % + QStringLiteral("parts") % + QStringLiteral("\n"); CAircraftParts lastParts; // default, so shown if parts are different from default QString tableRows("\n"); for (const PartsLog &log : logs) { - const bool changedParts = lastParts != log.parts; + const bool changedParts = (lastParts != log.parts); lastParts = log.parts; tableRows += - QLatin1String("") % - QLatin1String("") % log.callsign.asString() % QLatin1String("") % - QLatin1String("") % msSinceEpochToTime(log.timestamp) % QLatin1String("") % - (changedParts ? QLatin1String("*") : QLatin1String("")) % - QLatin1String("") % (log.empty ? QLatin1String("empty") : log.parts.toQString()) % QLatin1String(""); + QStringLiteral("") % + QStringLiteral("") % log.callsign.asString() % QStringLiteral("") % + QStringLiteral("") % msSinceEpochToTime(log.tsCurrent) % QStringLiteral("") % + (changedParts ? QStringLiteral("*") : QStringLiteral("")) % + QStringLiteral("") % (log.empty ? QStringLiteral("empty") : log.parts.toQString()) % QStringLiteral(""); } - tableRows += QLatin1String("\n"); - return QLatin1String("\n") % tableHeader % tableRows % QLatin1String("
\n"); + tableRows += QStringLiteral("\n"); + return QStringLiteral("\n") % tableHeader % tableRows % QStringLiteral("
\n"); } void CInterpolationLogger::clearLog() @@ -362,10 +390,86 @@ namespace BlackMisc return QDateTime::fromMSecsSinceEpoch(ms).toString(dateFormat); } + QString CInterpolationLogger::msSinceEpochToTimeAndTimestamp(qint64 ms) + { + return CInterpolationLogger::msSinceEpochToTime(ms) % QStringLiteral("/") % QString::number(ms); + } + QString CInterpolationLogger::msSinceEpochToTime(qint64 t1, qint64 t2, qint64 t3) { - if (t3 < 0) return QString("%1 %2").arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2)); - return QString("%1 %2 %3").arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2), msSinceEpochToTime(t3)); + static const QString s2("%1 %2"); + if (t3 < 0) { return s2.arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2)); } + + static const QString s3("%1 %2 %3"); + return s3.arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2), msSinceEpochToTime(t3)); + } + + QString CInterpolationLogger::SituationLog::toQString( + bool withCurrentSituation, bool withHints, bool withSetup, + bool withElevation, bool withOtherPositions, bool withDeltaTimes, const QString &separator) const + { + return QStringLiteral("CS: ") % callsign.asString() % separator % + QStringLiteral("ts: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) % + QStringLiteral(" type: ") % this->interpolationType() % + QStringLiteral(" gnd.fa.: ") % QString::number(groundFactor) % + ( + withHints ? + separator % QStringLiteral("hints: ") % usedHints.toQString(true) : + QStringLiteral("") + ) % + ( + withSetup ? + separator % QStringLiteral("setup: ") % usedSetup.toQString(true) : + QStringLiteral("") + ) % + ( + withElevation ? + separator % + QStringLiteral("transf.elv.: ") % QString::number(noTransferredElevations) : + QStringLiteral("") + ) % + ( + withDeltaTimes ? + separator % + QStringLiteral("int.time: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) % + QStringLiteral(" sample dt: ") % QString::number(deltaSampleTimesMs) % QStringLiteral("ms") % + QStringLiteral(" fr.[0-1]: ") % QString::number(simulationTimeFraction) % + QStringLiteral(" old pos.: ") % situationOld.getTimestampAndOffset(true) % + QStringLiteral(" new pos.: ") % situationNew.getTimestampAndOffset(true) : + QStringLiteral("") + ) % + ( + withCurrentSituation ? + separator % + QStringLiteral("sit.: ") % situationCurrent.toQString(true) : + QStringLiteral("") + ) % + ( + withOtherPositions ? + separator % + QStringLiteral("old: ") % situationOld.toQString(true) % + separator % + QStringLiteral("new: ") % situationNew.toQString(true) : + QStringLiteral("") + ); + } + + QString CInterpolationLogger::PartsLog::toQString(const QString &separator) const + { + return QStringLiteral("CS: ") % callsign.asString() % separator % + QStringLiteral("ts: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) % + separator % + QStringLiteral("parts: ") % parts.toQString(true); + } + + const QString &CInterpolationLogger::SituationLog::interpolationType() const + { + static const QString s("spline"); + static const QString l("linear"); + static const QString u("unknown"); + if (interpolator == 's') { return s; } + if (interpolator == 'l') { return l; } + return u; } } // namespace } // namespace diff --git a/src/blackmisc/simulation/interpolationlogger.h b/src/blackmisc/simulation/interpolationlogger.h index 83ec49da5..fd8d6c3b1 100644 --- a/src/blackmisc/simulation/interpolationlogger.h +++ b/src/blackmisc/simulation/interpolationlogger.h @@ -13,6 +13,7 @@ #define BLACKMISC_SIMULATION_INTERPOLATIONLOGGER_H #include "interpolationrenderingsetup.h" +#include "interpolationhints.h" #include "blackmisc/simulation/remoteaircraftprovider.h" #include "blackmisc/aviation/aircraftpartslist.h" #include "blackmisc/aviation/aircraftsituation.h" @@ -52,31 +53,51 @@ namespace BlackMisc static QString getLogDirectory(); //! Log entry for situation interpolation - struct SituationLog + struct BLACKMISC_EXPORT SituationLog { QChar interpolator; //!< what interpolator is used - qint64 timestamp = -1; //!< current timestamp + qint64 tsCurrent = -1; //!< current timestamp + qint64 tsInterpolated = -1; //!< timestamp interpolated double groundFactor = -1; //!< current ground factor double vtolAircraft = false; //!< VTOL aircraft - double deltaTimeMs = 0; //!< delta time to last situation - double simulationTimeFraction = -1; //!< time fraction, normally 0..1 - double deltaTimeFractionMs = -1; //!< delta time fraction + double simulationTimeFraction = -1; //!< time fraction, expected 0..1 + double deltaSampleTimesMs = -1; //!< delta time between samples (i.e. 2 situations) bool useParts = false; //!< supporting aircraft parts + int noTransferredElevations = 0; //!< transferred elevation to n situations Aviation::CCallsign callsign; //!< current callsign Aviation::CAircraftParts parts; //!< corresponding parts used in interpolator - Aviation::CAircraftSituation oldSituation; //!< old situation - Aviation::CAircraftSituation newSituation; //!< new situation - Aviation::CAircraftSituation currentSituation; //!< interpolated situation - PhysicalQuantities::CLength cgAboveGround; //!< center of gravity + Aviation::CAircraftSituation situationOld; //!< old situation + Aviation::CAircraftSituation situationNew; //!< new situation + Aviation::CAircraftSituation situationCurrent; //!< interpolated situation + PhysicalQuantities::CLength cgAboveGround; //!< center of gravity + CInterpolationAndRenderingSetup usedSetup; //!< used setup + CInterpolationHints usedHints; //!< hints + + //! Delta time between interpolation and current time + double deltaCurrentToInterpolatedTime() const + { + return static_cast(tsCurrent - tsInterpolated); + } + + //! Full name of interpolator + const QString &interpolationType() const; + + //! To string + QString toQString( + bool withCurrentSituation, bool withHints, bool withSetup, bool withElevation, + bool withOtherPositions, bool withDeltaTimes, const QString &separator = {" "}) const; }; //! Log entry for parts interpolation - struct PartsLog + struct BLACKMISC_EXPORT PartsLog { - qint64 timestamp = -1; //!< current timestamp + qint64 tsCurrent = -1; //!< current timestamp bool empty = false; //!< empty parts? Aviation::CCallsign callsign; //!< current callsign Aviation::CAircraftParts parts; //!< parts to be logged + + //! To string + QString toQString(const QString &separator = {" "}) const; }; //! Log current interpolation cycle, only stores in memory, for performance reasons @@ -108,6 +129,14 @@ namespace BlackMisc //! \threadsafe QList getPartsLog(const Aviation::CCallsign &cs) const; + //! Get last log + //! \threadsafe + SituationLog getLastSituationLog() const; + + //! Get last log + //! \threadsafe + SituationLog getLastSituationLog(const Aviation::CCallsign &cs) const; + //! Get last situation //! \threadsafe Aviation::CAircraftSituation getLastSituation() const; @@ -124,6 +153,14 @@ namespace BlackMisc //! \threadsafe Aviation::CAircraftParts getLastParts(const Aviation::CCallsign &cs) const; + //! Get last parts + //! \threadsafe + PartsLog getLastPartsLog() const; + + //! Get last parts + //! \threadsafe + PartsLog getLastPartsLog(const Aviation::CCallsign &cs) const; + //! File pattern for interpolation log static const QString &filePatternInterpolationLog(); @@ -149,12 +186,15 @@ namespace BlackMisc //! Create readable time static QString msSinceEpochToTime(qint64 ms); + //! Create readable time plus timestamp + static QString msSinceEpochToTimeAndTimestamp(qint64 ms); + //! Create readable time static QString msSinceEpochToTime(qint64 t1, qint64 t2, qint64 t3 = -1); - mutable QReadWriteLock m_lockSituations; //!< lock logging - mutable QReadWriteLock m_lockParts; //!< lock logging - int m_maxSituations = 2500; + mutable QReadWriteLock m_lockSituations; //!< lock logging situations + mutable QReadWriteLock m_lockParts; //!< lock logging parts + int m_maxSituations = 2500; //!< max.number of situations QList m_partsLogs; //!< logs of parts QList m_situationLogs; //!< logs of interpolation }; diff --git a/src/blackmisc/simulation/interpolationrenderingsetup.cpp b/src/blackmisc/simulation/interpolationrenderingsetup.cpp index 2938705e2..e6abc4efc 100644 --- a/src/blackmisc/simulation/interpolationrenderingsetup.cpp +++ b/src/blackmisc/simulation/interpolationrenderingsetup.cpp @@ -157,7 +157,7 @@ namespace BlackMisc return m_callsignsToLog; } - bool CInterpolationAndRenderingSetup::logCallsign(const Aviation::CCallsign &callsign) const + bool CInterpolationAndRenderingSetup::isLogCallsign(const Aviation::CCallsign &callsign) const { return m_callsignsToLog.contains(callsign); } diff --git a/src/blackmisc/simulation/interpolationrenderingsetup.h b/src/blackmisc/simulation/interpolationrenderingsetup.h index 8c1eeb0a6..90e9d0f56 100644 --- a/src/blackmisc/simulation/interpolationrenderingsetup.h +++ b/src/blackmisc/simulation/interpolationrenderingsetup.h @@ -114,7 +114,7 @@ namespace BlackMisc BlackMisc::Aviation::CCallsignSet getLogCallsigns() const; //! Log the given callsign? - bool logCallsign(const Aviation::CCallsign &callsign) const; + bool isLogCallsign(const Aviation::CCallsign &callsign) const; //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; diff --git a/src/blackmisc/simulation/interpolator.cpp b/src/blackmisc/simulation/interpolator.cpp index 2e01ad9f4..ef9bb069c 100644 --- a/src/blackmisc/simulation/interpolator.cpp +++ b/src/blackmisc/simulation/interpolator.cpp @@ -140,11 +140,10 @@ namespace BlackMisc { log.timestamp = currentTimeMsSinceEpoc; log.callsign = m_callsign; - log.cgAboveGround = hints.getCGAboveGround(); - log.vtolAircraft = hints.isVtolAircraft(); - log.currentSituation = currentSituation; - log.useParts = hints.hasAircraftParts(); - log.parts = hints.getAircraftParts(); + log.groundFactor = groundFactor; + log.situationCurrent = currentSituation; + log.usedHints = hints; + log.usedSetup = setup; m_logger->logInterpolation(log); }