7 #include <QStringBuilder>
21 using namespace swift::config;
23 using namespace swift::misc::aviation;
24 using namespace swift::misc::geo;
25 using namespace swift::misc::math;
26 using namespace swift::misc::physical_quantities;
27 using namespace swift::misc::simulation;
29 namespace swift::misc::simulation
31 CInterpolationLogger::CInterpolationLogger(QObject *parent) : QObject(parent)
33 this->setObjectName(
"CInterpolationLogger");
44 QList<SituationLog> situations;
45 QList<PartsLog> parts;
47 QReadLocker l(&m_lockSituations);
48 situations = m_situationLogs;
51 QReadLocker l(&m_lockParts);
55 QPointer<CInterpolationLogger> myself(
this);
60 if (
clearLog && myself) { myself->clearLog(); }
67 QStringList files({
"",
"" });
70 if (!logs.exists()) {
return files; }
72 const QStringList interpolations =
75 const QStringList parts = logs.entryList(QStringList({
filePatternPartsLog() }), QDir::NoFilter, QDir::Time);
82 CStatusMessageList CInterpolationLogger::writeLogFiles(
const QList<SituationLog> &interpolation,
83 const QList<PartsLog> &parts)
85 if (parts.isEmpty() && interpolation.isEmpty())
89 static const QString html = QStringLiteral(
"Entries: %1\n\n%2");
93 const QString ts = QDateTime::currentDateTimeUtc().toString(
"yyyyMMddhhmmss");
95 const QString htmlInterpolation = CInterpolationLogger::getHtmlInterpolationLog(interpolation);
96 if (!htmlInterpolation.isEmpty())
103 htmlTemplate.arg(html.arg(interpolation.size()).arg(htmlInterpolation)), fn);
104 msgs.
push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
107 const QString htmlParts = CInterpolationLogger::getHtmlPartsLog(parts);
108 if (!htmlParts.isEmpty())
115 msgs.
push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
118 QString kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlChangedSituations(interpolation));
122 QStringLiteral(
"%1_changedSituations.kml").arg(ts));
124 msgs.
push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
127 kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlInterpolatedSituations(interpolation));
131 QStringLiteral(
"%1_interpolatedSituations.kml").arg(ts));
133 msgs.
push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
136 kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlElevations(interpolation));
140 QStringLiteral(
"%1_elevations.kml").arg(ts));
142 msgs.
push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
148 CStatusMessage CInterpolationLogger::logStatusFileWriting(
bool success,
const QString &fileName)
159 QWriteLocker l(&m_lockSituations);
160 m_situationLogs.push_back(log);
161 if (m_situationLogs.size() > m_maxSituations) { m_situationLogs.removeFirst(); }
166 QWriteLocker l(&m_lockParts);
167 m_partsLogs.push_back(log);
172 QReadLocker l(&m_lockSituations);
173 m_maxSituations = max;
178 QReadLocker l(&m_lockSituations);
179 return m_situationLogs;
184 QReadLocker l(&m_lockParts);
191 QList<SituationLog> logs;
194 if (log.callsign != cs) {
continue; }
203 QList<PartsLog> logs;
206 if (log.callsign != cs) {
continue; }
214 QReadLocker l(&m_lockSituations);
215 if (m_situationLogs.isEmpty()) {
return SituationLog(); }
216 return m_situationLogs.last();
228 QReadLocker l(&m_lockSituations);
230 return m_situationLogs.last().situationCurrent;
237 return copy.last().situationCurrent;
242 QReadLocker l(&m_lockParts);
244 return m_partsLogs.last().parts;
251 return copy.last().parts;
256 QReadLocker l(&m_lockParts);
257 if (m_partsLogs.isEmpty()) {
return PartsLog(); }
258 return m_partsLogs.last();
264 if (copy.isEmpty()) {
return PartsLog(); }
270 static const QString p(
"*interpolation.html");
276 static const QString p(
"*parts.html");
286 QString CInterpolationLogger::getHtmlInterpolationLog(
const QList<SituationLog> &logs)
288 if (logs.isEmpty()) {
return {}; }
289 static const QString tableHeader =
290 QStringLiteral(u
"<thead><tr>"
291 u
"<th title=\"changed situation\">cs.</th><th>Int</th>"
292 u
"<th title=\"recalculated interpolant\">recalc</th>"
293 u
"<th>CS</th><th>timestamp</th><th>since</th>"
294 u
"<th>ts old</th><th>ts new</th><th>ts cur</th>"
295 u
"<th>Interpolation ts.</th><th>Sample Δt</th><th>fraction</th>"
296 u
"<th>lat.old</th><th>lat.new</th><th>lat.cur</th>"
297 u
"<th>lng.old</th><th>lng.new</th><th>lng.cur</th>"
298 u
"<th>alt.old</th><th>alt.2nd</th><th>alt.new</th><th>alt.cur</th>"
299 u
"<th>elv.old</th><th>elv.2nd</th><th>elv.new</th><th>elv.cur</th>"
300 u
"<th>gnd.factor</th>"
301 u
"<th>onGnd.old</th><th>onGnd.new</th><th>onGnd.cur</th>"
303 u
"<th>parts</th><th title=\"changed parts\">cp.</th><th>parts details</th>"
311 QString tableRows(
"<tbody>\n");
318 const bool changedParts = (lastParts != log.parts);
320 lastParts = log.parts;
326 (changedNewPosition ? QStringLiteral(
"<td class=\"changed\">*</td>") : QStringLiteral(
"<td></td>")) %
327 u
"<td>" % log.interpolator % u
"</td>" % u
"<td>" %
boolToYesNo(log.interpolantRecalc) %
330 log.callsign.asString() % u
"</td>" % u
"<td>" %
msSinceEpochToTime(log.tsCurrent) % u
"</td>" % u
"<td>" %
331 QString::number(log.tsCurrent - firstLog.tsCurrent) % u
"</td>" %
333 u
"<td class=\"old\">" % situationOld.getTimestampAndOffset(true) % u
"</td>" % u
"<td class=\"new\">" %
334 situationNew.getTimestampAndOffset(true) % u
"</td>" % u
"<td class=\"cur\">" %
335 log.situationCurrent.getTimestampAndOffset(true) % u
"</td>" %
338 QString::number(log.deltaSampleTimesMs) % u
"ms</td>" % u
"<td>" % QString::number(log.simTimeFraction) %
342 u
"<td class=\"old\">" % situationOld.latitudeAsString() % u
"</td>" % u
"<td class=\"new\">" %
343 situationNew.latitudeAsString() % u
"</td>" % u
"<td class=\"cur\">" %
344 log.situationCurrent.latitudeAsString() % u
"</td>" %
346 u
"<td class=\"old\">" % situationOld.longitudeAsString() % u
"</td>" % u
"<td class=\"new\">" %
347 situationNew.longitudeAsString() % u
"</td>" % u
"<td class=\"cur\">" %
348 log.situationCurrent.longitudeAsString() % u
"</td>" %
351 u
"<td class=\"old\">" % situationOld.getAltitude().valueRoundedWithUnit(ft, 1) % u
"</td>" %
352 u
"<td class=\"old\">" % situation2nd.getAltitude().valueRoundedWithUnit(ft, 1) % u
"</td>" %
353 u
"<td class=\"new\">" % situationNew.getAltitude().valueRoundedWithUnit(ft, 1) % u
"</td>" %
354 u
"<td class=\"cur\">" % log.situationCurrent.getAltitude().valueRoundedWithUnit(ft, 1) % u
"</td>" %
356 u
"<td class=\"old\">" % situationOld.getGroundElevation().valueRoundedWithUnit(ft, 1) % u
" " %
357 situationOld.getGroundElevationInfoAsString() % u
"</td>" % u
"<td class=\"old\">" %
358 situation2nd.getGroundElevation().valueRoundedWithUnit(ft, 1) % u
" " %
359 situation2nd.getGroundElevationInfoAsString() % u
"</td>" % u
"<td class=\"new\">" %
360 situationNew.getGroundElevation().valueRoundedWithUnit(ft, 1) % u
" " %
361 situationNew.getGroundElevationInfoAsString() % u
"</td>" % u
"<td class=\"cur\">" %
362 log.situationCurrent.getGroundElevation().valueRoundedWithUnit(ft, 1) % u
" " %
363 log.situationCurrent.getGroundElevationInfoAsString() % u
"</td>" %
365 u
"<td>" % QString::number(log.groundFactor) % u
"</td>" % u
"<td class=\"old\">" %
366 situationOld.getOnGroundInfo().toQString() % u
"</td>" % u
"<td class=\"new\">" %
367 situationNew.getOnGroundInfo().toQString() % u
"</td>" % u
"<td class=\"cur\">" %
368 log.situationCurrent.getOnGroundInfo().toQString() % u
"</td>" %
371 u
"<td>" % log.cgAboveGround.valueRoundedWithUnit(ft, 0) % u
"</td>" % u
"<td>" %
372 boolToYesNo(log.useParts) % u
"</td>" % (changedParts ? u
"<td class=\"changed\">*</td>" : u
"<td></td>") %
374 (!log.useParts || log.parts.isNull() ? QString() : log.parts.toQString(true).toHtmlEscaped()) %
375 u
"</td>" % u
"</tr>\n";
378 tableRows += QStringLiteral(
"</tbody>\n");
379 return u
"<table class=\"small\">\n" % tableHeader % tableRows % u
"</table>\n";
382 QString CInterpolationLogger::getKmlChangedSituations(
const QList<SituationLog> &logs)
384 if (logs.isEmpty()) {
return {}; }
387 qint64 newPosTs = -1;
395 const bool recalc = log.interpolantRecalc;
396 if (!changedNewPosition && !recalc) {
continue; }
398 kml += CKmlUtils::asPlacemark(QStringLiteral(
"%1: %2 new pos: %3 recalc: %4")
402 situationNew.
toQString(
true), situationNew, s) %
409 QString CInterpolationLogger::getKmlElevations(
const QList<SituationLog> &logs)
411 if (logs.isEmpty()) {
return {}; }
414 qint64 newPosTs = -1;
422 const bool recalc = log.interpolantRecalc;
423 if (!changedNewPosition && !recalc) {
continue; }
426 kml += CKmlUtils::asPlacemark(QStringLiteral(
"%1: %2 %3 info: %4 alt.cor: %5")
439 QString CInterpolationLogger::getKmlInterpolatedSituations(
const QList<SituationLog> &logs)
441 if (logs.isEmpty()) {
return {}; }
448 coordinates += CKmlUtils::asRawCoordinates(situation, s.withAltitude) % u
"\n";
451 return u
"<Placemark>\n"
452 u
"<name>Interpolation " %
453 QString::number(logs.size()) % u
"entries</name>\n" % CKmlUtils::asLineString(coordinates, s) %
457 QString CInterpolationLogger::getHtmlPartsLog(
const QList<PartsLog> &logs)
459 if (logs.isEmpty()) {
return {}; }
460 static const QString tableHeader = QStringLiteral(u
"<thead><tr>"
461 u
"<th>CS</th><th>timestamp</th>"
467 QString tableRows(
"<tbody>\n");
470 const bool changedParts = (lastParts != log.parts);
471 lastParts = log.parts;
472 tableRows += u
"<tr><td>" % log.callsign.asString() % u
"</td>" % u
"<td>" %
474 (changedParts ? u
"<td class=\"changed\">*</td>" : u
"<td></td>") % u
"<td>" %
475 (log.empty ? QStringLiteral(
"empty") : log.parts.toQString()) % u
"</td></tr>";
477 tableRows +=
"</tbody>\n";
478 return QStringLiteral(
"<table class=\"small\">\n") % tableHeader % tableRows % QStringLiteral(
"</table>\n");
484 QWriteLocker l(&m_lockSituations);
485 m_situationLogs.clear();
488 QWriteLocker l(&m_lockParts);
495 static const QString dateFormat(
"hh:mm:ss.zzz");
496 return QDateTime::fromMSecsSinceEpoch(ms).toString(dateFormat);
512 bool withOtherPositions,
bool withDeltaTimes,
const QString &separator)
const
518 (withElevation ? u
"Elev.info: " %
elevationInfo % u
" scenery os: " %
540 (withOtherPositions ? separator % u
"old: " % situationOldInterpolation.
toQString(
true) % separator %
541 u
"new: " % situationNewInterpolation.
toQString(
true) :
554 static const QString s(
"spline");
555 static const QString l(
"linear");
556 static const QString u(
"unknown");
563 # pragma GCC diagnostic push
564 # pragma GCC diagnostic ignored "-Wstrict-overflow"
574 # pragma GCC diagnostic pop
static bool writeStringToFile(const QString &content, const QString &fileNameAndPath)
Write string to text file.
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
static QString readFileToString(const QString &fileNameAndPath)
Read file into string.
static const QString & interpolator()
Interpolator.
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
size_type size() const
Returns number of elements in the sequence.
int size_type
STL compatibility.
void push_back(const T &value)
Appends an element at the end of the sequence.
Streamable status message, e.g.
Status messages, e.g. from Core -> GUI.
static const QString & htmlTemplateFilePath()
HTML template.
static const QString & logDirectory()
Directory for log files.
Class for doing some arbitrary parcel of work in its own thread.
static CWorker * fromTask(QObject *owner, const QString &name, F &&task)
Returns a new worker object which lives in a new thread.
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
QString getFormattedUtcTimestampHmsz() const
As hh:mm:ss.zzz.
QString getTimestampAndOffset(bool formatted) const
Timestamp and offset.
Value object encapsulating information of aircraft's parts.
Value object encapsulating information of an aircraft's situation.
QString getGroundElevationAndInfo() const
Ground elevation plus info.
bool hasGroundElevation() const
Is ground elevation value available.
static const CAircraftSituation & null()
Null situation.
const geo::CElevationPlane & getGroundElevationPlane() const
Elevation of the ground directly beneath.
Value object encapsulating information of a callsign.
const QString & asString() const
Get callsign (normalized)
QString toQString(bool i18n=false) const
Cast as QString.
Specialized class for distance units (meter, foot, nautical miles).
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
Record internal state of interpolator for debugging.
static const QStringList & filePatterns()
All log.file patterns.
static QString msSinceEpochToTime(qint64 ms)
Create readable time.
static const QString & filePatternPartsLog()
File pattern for parts log.
void setMaxSituations(int max)
Max.situations logged.
static QString msSinceEpochToTimeAndTimestamp(qint64 ms)
Create readable time plus timestamp.
aviation::CAircraftParts getLastParts() const
Get last parts.
static QString getLogDirectory()
Get the log directory.
PartsLog getLastPartsLog() const
Get last parts.
static const QStringList & getLogCategories()
Log categories.
void logInterpolation(const SituationLog &log)
Log current interpolation cycle, only stores in memory, for performance reasons.
SituationLog getLastSituationLog() const
Get last log.
static QStringList getLatestLogFiles()
Latest log files: 0: Interpolation / 1: Parts.
CWorker * writeLogInBackground(bool clearLog)
Write a log in background.
aviation::CAircraftSituation getLastSituation() const
Get last situation.
QList< PartsLog > getPartsLog() const
All parts logs.
static const QString & filePatternInterpolationLog()
File pattern for interpolation log.
void clearLog()
Clear log file.
QList< SituationLog > getSituationsLog() const
All situation logs.
void logParts(const PartsLog &log)
Log current parts cycle, only stores in memory, for performance reasons.
Free functions in swift::misc.
SWIFT_MISC_EXPORT const QString & boolToYesNo(bool v)
Bool to yes/no.
Log entry for parts interpolation.
int noNetworkParts
available network situations
aviation::CAircraftParts parts
parts to be logged
qint64 tsCurrent
current timestamp
aviation::CCallsign callsign
current callsign
QString toQString(const QString &separator={ " " }) const
To string.
Log entry for situation interpolation.
double deltaCurrentToInterpolatedTime() const
Delta time between interpolation and current time.
QChar interpolator
what interpolator is used
aviation::CAircraftSituationList interpolationSituations
the interpolator uses 2, 3 situations (latest at end)
const aviation::CAircraftSituation & newestInterpolationSituation() const
The newest situation.
physical_quantities::CLength sceneryOffset
scenery offset
aviation::CCallsign callsign
current callsign
aviation::CAircraftSituation situationCurrent
interpolated situation
int noNetworkSituations
available network situations
const aviation::CAircraftSituation & oldestInterpolationSituation() const
The oldest situation.
qint64 tsInterpolated
timestamp interpolated
QString elevationInfo
info about elevation retrieval
double deltaSampleTimesMs
delta time between samples (i.e.
aviation::CAircraftSituationChange change
change
double groundFactor
current ground factor
QString toQString(bool withSetup, bool withCurrentSituation, bool withElevation, bool withOtherPositions, bool withDeltaTimes, const QString &separator={ " " }) const
To string.
physical_quantities::CLength cgAboveGround
center of gravity (CG)
double simTimeFraction
time fraction, expected 0..1
const QString & interpolationType() const
Full name of interpolator.
qint64 tsCurrent
current timestamp
const aviation::CAircraftSituation & secondInterpolationSituation() const
The second latest situation (spline)
QString altCorrection
info about altitude correction as CAircraftSituation::AltitudeCorrection
int noInvalidSituations
invalid situations, missing situations for timestampd
CInterpolationAndRenderingSetupPerCallsign usedSetup
used setup