swift
interpolationlogger.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDateTime>
7 #include <QStringBuilder>
8 
9 #include "config/buildconfig.h"
10 #include "misc/aviation/callsign.h"
11 #include "misc/directoryutils.h"
12 #include "misc/geo/kmlutils.h"
13 #include "misc/logmessage.h"
14 #include "misc/pq/angle.h"
15 #include "misc/pq/length.h"
16 #include "misc/pq/units.h"
17 #include "misc/stringutils.h"
18 #include "misc/swiftdirectories.h"
19 #include "misc/worker.h"
20 
21 using namespace swift::config;
22 using namespace swift::misc;
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;
28 
29 namespace swift::misc::simulation
30 {
31  CInterpolationLogger::CInterpolationLogger(QObject *parent) : QObject(parent)
32  {
33  this->setObjectName("CInterpolationLogger");
34  }
35 
37  {
38  static const QStringList cats { CLogCategories::interpolator() };
39  return cats;
40  }
41 
43  {
44  QList<SituationLog> situations;
45  QList<PartsLog> parts;
46  {
47  QReadLocker l(&m_lockSituations);
48  situations = m_situationLogs;
49  }
50  {
51  QReadLocker l(&m_lockParts);
52  parts = m_partsLogs;
53  }
54 
55  QPointer<CInterpolationLogger> myself(this);
56  CWorker *worker = CWorker::fromTask(this, "WriteInterpolationLog", [situations, parts, myself, clearLog]() {
57  const CStatusMessageList msg = CInterpolationLogger::writeLogFiles(situations, parts);
59 
60  if (clearLog && myself) { myself->clearLog(); }
61  });
62  return worker;
63  }
64 
66  {
67  QStringList files({ "", "" });
68  const QString logDir = CSwiftDirectories::logDirectory();
69  QDir logs(logDir);
70  if (!logs.exists()) { return files; }
71  logs.setNameFilters(filePatterns());
72  const QStringList interpolations =
73  logs.entryList(QStringList({ filePatternInterpolationLog() }), QDir::NoFilter, QDir::Time);
74  if (!interpolations.isEmpty()) { files[0] = CFileUtils::appendFilePaths(logDir, interpolations.first()); }
75  const QStringList parts = logs.entryList(QStringList({ filePatternPartsLog() }), QDir::NoFilter, QDir::Time);
76  if (!parts.isEmpty()) { files[1] = CFileUtils::appendFilePaths(logDir, parts.first()); }
77  return files;
78  }
79 
81 
82  CStatusMessageList CInterpolationLogger::writeLogFiles(const QList<SituationLog> &interpolation,
83  const QList<PartsLog> &parts)
84  {
85  if (parts.isEmpty() && interpolation.isEmpty())
86  {
87  return CStatusMessage(static_cast<CInterpolationLogger *>(nullptr)).warning(u"No data for log");
88  }
89  static const QString html = QStringLiteral("Entries: %1\n\n%2");
91 
92  CStatusMessageList msgs;
93  const QString ts = QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmss");
94 
95  const QString htmlInterpolation = CInterpolationLogger::getHtmlInterpolationLog(interpolation);
96  if (!htmlInterpolation.isEmpty())
97  {
98  QString file = filePatternInterpolationLog();
99  file.remove('*');
100  const QString fn =
101  CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), QStringLiteral("%1 %2").arg(ts, file));
102  const bool s = CFileUtils::writeStringToFile(
103  htmlTemplate.arg(html.arg(interpolation.size()).arg(htmlInterpolation)), fn);
104  msgs.push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
105  }
106 
107  const QString htmlParts = CInterpolationLogger::getHtmlPartsLog(parts);
108  if (!htmlParts.isEmpty())
109  {
110  QString file = filePatternPartsLog();
111  file.remove('*');
112  const QString fn =
113  CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), QStringLiteral("%1 %2").arg(ts, file));
114  const bool s = CFileUtils::writeStringToFile(htmlTemplate.arg(html.arg(parts.size()).arg(htmlParts)), fn);
115  msgs.push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
116  }
117 
118  QString kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlChangedSituations(interpolation));
119  if (!kml.isEmpty())
120  {
122  QStringLiteral("%1_changedSituations.kml").arg(ts));
123  const bool s = CFileUtils::writeStringToFile(kml, fn);
124  msgs.push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
125  }
126 
127  kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlInterpolatedSituations(interpolation));
128  if (!kml.isEmpty())
129  {
131  QStringLiteral("%1_interpolatedSituations.kml").arg(ts));
132  const bool s = CFileUtils::writeStringToFile(kml, fn);
133  msgs.push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
134  }
135 
136  kml = CKmlUtils::wrapAsKmlDocument(CInterpolationLogger::getKmlElevations(interpolation));
137  if (!kml.isEmpty())
138  {
140  QStringLiteral("%1_elevations.kml").arg(ts));
141  const bool s = CFileUtils::writeStringToFile(kml, fn);
142  msgs.push_back(CInterpolationLogger::logStatusFileWriting(s, fn));
143  }
144 
145  return msgs;
146  }
147 
148  CStatusMessage CInterpolationLogger::logStatusFileWriting(bool success, const QString &fileName)
149  {
150  return success ?
151  CStatusMessage(static_cast<CInterpolationLogger *>(nullptr)).info(u"Written log file '%1'")
152  << fileName :
153  CStatusMessage(static_cast<CInterpolationLogger *>(nullptr)).error(u"Failed to write log file '%1'")
154  << fileName;
155  }
156 
158  {
159  QWriteLocker l(&m_lockSituations);
160  m_situationLogs.push_back(log);
161  if (m_situationLogs.size() > m_maxSituations) { m_situationLogs.removeFirst(); }
162  }
163 
165  {
166  QWriteLocker l(&m_lockParts);
167  m_partsLogs.push_back(log);
168  }
169 
171  {
172  QReadLocker l(&m_lockSituations);
173  m_maxSituations = max;
174  }
175 
176  QList<SituationLog> CInterpolationLogger::getSituationsLog() const
177  {
178  QReadLocker l(&m_lockSituations);
179  return m_situationLogs;
180  }
181 
182  QList<PartsLog> CInterpolationLogger::getPartsLog() const
183  {
184  QReadLocker l(&m_lockParts);
185  return m_partsLogs;
186  }
187 
188  QList<SituationLog> CInterpolationLogger::getSituationsLog(const CCallsign &cs) const
189  {
190  const QList<SituationLog> copy(this->getSituationsLog());
191  QList<SituationLog> logs;
192  for (const SituationLog &log : copy)
193  {
194  if (log.callsign != cs) { continue; }
195  logs.push_back(log);
196  }
197  return logs;
198  }
199 
200  QList<PartsLog> CInterpolationLogger::getPartsLog(const CCallsign &cs) const
201  {
202  const QList<PartsLog> copy(this->getPartsLog());
203  QList<PartsLog> logs;
204  for (const PartsLog &log : copy)
205  {
206  if (log.callsign != cs) { continue; }
207  logs.push_back(log);
208  }
209  return logs;
210  }
211 
213  {
214  QReadLocker l(&m_lockSituations);
215  if (m_situationLogs.isEmpty()) { return SituationLog(); }
216  return m_situationLogs.last();
217  }
218 
220  {
221  const QList<SituationLog> copy(this->getSituationsLog(cs));
222  if (copy.isEmpty()) { return SituationLog(); }
223  return copy.last();
224  }
225 
227  {
228  QReadLocker l(&m_lockSituations);
229  if (m_situationLogs.isEmpty()) { return CAircraftSituation(); }
230  return m_situationLogs.last().situationCurrent;
231  }
232 
234  {
235  const QList<SituationLog> copy(this->getSituationsLog(cs));
236  if (copy.isEmpty()) { return CAircraftSituation(); }
237  return copy.last().situationCurrent;
238  }
239 
241  {
242  QReadLocker l(&m_lockParts);
243  if (m_partsLogs.isEmpty()) { return CAircraftParts(); }
244  return m_partsLogs.last().parts;
245  }
246 
248  {
249  const QList<PartsLog> copy(this->getPartsLog(cs));
250  if (copy.isEmpty()) { return CAircraftParts(); }
251  return copy.last().parts;
252  }
253 
255  {
256  QReadLocker l(&m_lockParts);
257  if (m_partsLogs.isEmpty()) { return PartsLog(); }
258  return m_partsLogs.last();
259  }
260 
262  {
263  const QList<PartsLog> copy(this->getPartsLog(cs));
264  if (copy.isEmpty()) { return PartsLog(); }
265  return copy.last();
266  }
267 
269  {
270  static const QString p("*interpolation.html");
271  return p;
272  }
273 
275  {
276  static const QString p("*parts.html");
277  return p;
278  }
279 
281  {
282  static const QStringList l({ filePatternInterpolationLog(), filePatternPartsLog() });
283  return l;
284  }
285 
286  QString CInterpolationLogger::getHtmlInterpolationLog(const QList<SituationLog> &logs)
287  {
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 &Delta;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>"
302  u"<th>CG</th>"
303  u"<th>parts</th><th title=\"changed parts\">cp.</th><th>parts details</th>"
304  u"</tr></thead>\n");
305 
306  static const CLengthUnit ft = CLengthUnit::ft();
307  const SituationLog firstLog = logs.first();
308  qint64 newPosTs = firstLog.newestInterpolationSituation().getMSecsSinceEpoch();
309  CAircraftParts lastParts; // default, so shown if parts are different from default
310 
311  QString tableRows("<tbody>\n");
312  for (const SituationLog &log : logs)
313  {
314  const CAircraftSituation situationOld = log.oldestInterpolationSituation();
315  const CAircraftSituation situationNew = log.newestInterpolationSituation();
316  const CAircraftSituation situation2nd = log.secondInterpolationSituation();
317  const bool changedNewPosition = (newPosTs != situationNew.getMSecsSinceEpoch());
318  const bool changedParts = (lastParts != log.parts);
319  newPosTs = situationNew.getMSecsSinceEpoch();
320  lastParts = log.parts;
321 
322  // fixme KB 2018/12 comment and tableRows can be removed if there are no further issues
323  // concatenating in multiple steps, otherwise C4503 warnings
324  tableRows +=
325  u"<tr>" %
326  (changedNewPosition ? QStringLiteral("<td class=\"changed\">*</td>") : QStringLiteral("<td></td>")) %
327  u"<td>" % log.interpolator % u"</td>" % u"<td>" % boolToYesNo(log.interpolantRecalc) %
328  u"</td>"
329  u"<td>" %
330  log.callsign.asString() % u"</td>" % u"<td>" % msSinceEpochToTime(log.tsCurrent) % u"</td>" % u"<td>" %
331  QString::number(log.tsCurrent - firstLog.tsCurrent) % u"</td>" %
332 
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>" %
336 
337  u"<td>" % msSinceEpochToTime(log.tsInterpolated) % u"</td>" % u"<td>" %
338  QString::number(log.deltaSampleTimesMs) % u"ms</td>" % u"<td>" % QString::number(log.simTimeFraction) %
339  u"</td>" %
340 
341  // tableRows +=
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>" %
345 
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>" %
349 
350  // tableRows +=
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>" %
355 
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>" %
364 
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>" %
369 
370  // tableRows +=
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>") %
373  u"<td>" %
374  (!log.useParts || log.parts.isNull() ? QString() : log.parts.toQString(true).toHtmlEscaped()) %
375  u"</td>" % u"</tr>\n";
376  }
377 
378  tableRows += QStringLiteral("</tbody>\n");
379  return u"<table class=\"small\">\n" % tableHeader % tableRows % u"</table>\n";
380  }
381 
382  QString CInterpolationLogger::getKmlChangedSituations(const QList<SituationLog> &logs)
383  {
384  if (logs.isEmpty()) { return {}; }
385 
386  QString kml;
387  qint64 newPosTs = -1;
388  int n = 1;
389  const CKmlUtils::KMLSettings s(true, true);
390 
391  for (const SituationLog &log : logs)
392  {
393  const CAircraftSituation situationNew = log.newestInterpolationSituation();
394  const bool changedNewPosition = (newPosTs != situationNew.getMSecsSinceEpoch());
395  const bool recalc = log.interpolantRecalc;
396  if (!changedNewPosition && !recalc) { continue; }
397  newPosTs = situationNew.getMSecsSinceEpoch();
398  kml += CKmlUtils::asPlacemark(QStringLiteral("%1: %2 new pos: %3 recalc: %4")
399  .arg(n++)
400  .arg(situationNew.getFormattedUtcTimestampHmsz(),
401  boolToYesNo(changedNewPosition), boolToYesNo(recalc)),
402  situationNew.toQString(true), situationNew, s) %
403  u"\n";
404  }
405 
406  return kml;
407  }
408 
409  QString CInterpolationLogger::getKmlElevations(const QList<SituationLog> &logs)
410  {
411  if (logs.isEmpty()) { return {}; }
412 
413  QString kml;
414  qint64 newPosTs = -1;
415  int n = 1;
416  const CKmlUtils::KMLSettings s(true, true);
417 
418  for (const SituationLog &log : logs)
419  {
420  const CAircraftSituation situationNew = log.newestInterpolationSituation();
421  const bool changedNewPosition = (newPosTs != situationNew.getMSecsSinceEpoch());
422  const bool recalc = log.interpolantRecalc;
423  if (!changedNewPosition && !recalc) { continue; }
424  if (!situationNew.hasGroundElevation()) { continue; }
425  newPosTs = situationNew.getMSecsSinceEpoch();
426  kml += CKmlUtils::asPlacemark(QStringLiteral("%1: %2 %3 info: %4 alt.cor: %5")
427  .arg(n++)
428  .arg(situationNew.getFormattedUtcTimestampHmsz(),
429  situationNew.getGroundElevationAndInfo(), log.elevationInfo,
430  log.altCorrection),
431  situationNew.getGroundElevationPlane().toQString(true),
432  situationNew.getGroundElevationPlane(), s) %
433  u"\n";
434  }
435 
436  return kml;
437  }
438 
439  QString CInterpolationLogger::getKmlInterpolatedSituations(const QList<SituationLog> &logs)
440  {
441  if (logs.isEmpty()) { return {}; }
442 
443  const CKmlUtils::KMLSettings s(true, false);
444  QString coordinates;
445  for (const SituationLog &log : logs)
446  {
447  const CAircraftSituation situation = log.situationCurrent;
448  coordinates += CKmlUtils::asRawCoordinates(situation, s.withAltitude) % u"\n";
449  }
450 
451  return u"<Placemark>\n"
452  u"<name>Interpolation " %
453  QString::number(logs.size()) % u"entries</name>\n" % CKmlUtils::asLineString(coordinates, s) %
454  u"</Placemark>\n";
455  }
456 
457  QString CInterpolationLogger::getHtmlPartsLog(const QList<PartsLog> &logs)
458  {
459  if (logs.isEmpty()) { return {}; }
460  static const QString tableHeader = QStringLiteral(u"<thead><tr>"
461  u"<th>CS</th><th>timestamp</th>"
462  u"<th>c.</th>"
463  u"<th>parts</th>"
464  u"</tr></thead>\n");
465 
466  CAircraftParts lastParts; // default, so shown if parts are different from default
467  QString tableRows("<tbody>\n");
468  for (const PartsLog &log : logs)
469  {
470  const bool changedParts = (lastParts != log.parts);
471  lastParts = log.parts;
472  tableRows += u"<tr><td>" % log.callsign.asString() % u"</td>" % u"<td>" %
473  msSinceEpochToTime(log.tsCurrent) % 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>";
476  }
477  tableRows += "</tbody>\n";
478  return QStringLiteral("<table class=\"small\">\n") % tableHeader % tableRows % QStringLiteral("</table>\n");
479  }
480 
482  {
483  {
484  QWriteLocker l(&m_lockSituations);
485  m_situationLogs.clear();
486  }
487  {
488  QWriteLocker l(&m_lockParts);
489  m_partsLogs.clear();
490  }
491  }
492 
494  {
495  static const QString dateFormat("hh:mm:ss.zzz");
496  return QDateTime::fromMSecsSinceEpoch(ms).toString(dateFormat);
497  }
498 
500  {
501  return CInterpolationLogger::msSinceEpochToTime(ms) % u"/" % QString::number(ms);
502  }
503 
504  QString CInterpolationLogger::msSinceEpochToTime(qint64 t1, qint64 t2, qint64 t3)
505  {
506  if (t3 < 0) { return QStringLiteral("%1 %2").arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2)); }
507 
508  return QStringLiteral("%1 %2 %3").arg(msSinceEpochToTime(t1), msSinceEpochToTime(t2), msSinceEpochToTime(t3));
509  }
510 
511  QString SituationLog::toQString(bool withSetup, bool withCurrentSituation, bool withElevation,
512  bool withOtherPositions, bool withDeltaTimes, const QString &separator) const
513  {
514  const CAircraftSituation situationOldInterpolation = this->oldestInterpolationSituation();
515  const CAircraftSituation situationNewInterpolation = this->newestInterpolationSituation();
516 
517  return (withSetup ? u"setup: " % usedSetup.toQString(true) % separator : QString()) %
518  (withElevation ? u"Elev.info: " % elevationInfo % u" scenery os: " %
519  sceneryOffset.valueRoundedWithUnit(1) % separator :
520  QString()) %
521  u"change: " % change.toQString(true) % separator % u"Interpolation CS: " % callsign.asString() %
522  separator % u"ts: " % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) % u" | type: " %
523  this->interpolationType() % u" | gnd.fa.: " % QString::number(groundFactor) % u" | CG: " %
524  cgAboveGround.valueRoundedWithUnit(CLengthUnit::m(), 1) % u" " %
525  cgAboveGround.valueRoundedWithUnit(CLengthUnit::ft(), 1) % u" | alt.cor.: " % altCorrection %
526  u" | #nw.sit.: " % QString::number(noNetworkSituations) % u" | #invalid: " %
527  QString::number(noInvalidSituations) %
528  (withDeltaTimes ?
531  u" | dt.cur.int.: " % QString::number(deltaCurrentToInterpolatedTime()) % u"ms" %
532  u" | sample dt: " % QString::number(deltaSampleTimesMs) % u"ms" % u" | fr.[0-1]: " %
533  QString::number(simTimeFraction) % u" | old int.pos.: " %
534  situationOldInterpolation.getTimestampAndOffset(true) % u" | new int.pos.: " %
535  situationNewInterpolation.getTimestampAndOffset(true) % u" | #int.pos.: " %
536  QString::number(interpolationSituations.size()) :
537  QString()) %
538  (withCurrentSituation ? separator % u"cur.sit.(interpolated): " % situationCurrent.toQString(true) :
539  QString()) %
540  (withOtherPositions ? separator % u"old: " % situationOldInterpolation.toQString(true) % separator %
541  u"new: " % situationNewInterpolation.toQString(true) :
542  QString());
543  }
544 
545  QString PartsLog::toQString(const QString &separator) const
546  {
547  return u"CS: " % callsign.asString() % separator % u"ts: " %
549  QString::number(noNetworkParts) % separator % u"parts: " % parts.toQString(true);
550  }
551 
552  const QString &SituationLog::interpolationType() const
553  {
554  static const QString s("spline");
555  static const QString l("linear");
556  static const QString u("unknown");
557  if (interpolator == 's') { return s; }
558  if (interpolator == 'l') { return l; }
559  return u;
560  }
561 
562 #ifdef __GNUC__
563 # pragma GCC diagnostic push
564 # pragma GCC diagnostic ignored "-Wstrict-overflow"
565 #endif
567  {
570  interpolationSituations.size() - 2; // 2nd latest, latest at end
571  return interpolationSituations[i];
572  }
573 #ifdef __GNUC__
574 # pragma GCC diagnostic pop
575 #endif
576 
577 } // namespace swift::misc::simulation
static bool writeStringToFile(const QString &content, const QString &fileNameAndPath)
Write string to text file.
Definition: fileutils.cpp:40
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:95
static QString readFileToString(const QString &fileNameAndPath)
Read file into string.
Definition: fileutils.cpp:68
static const QString & interpolator()
Interpolator.
Definition: logcategories.h:73
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.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
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.
Definition: worker.h:188
static CWorker * fromTask(QObject *owner, const QString &name, F &&task)
Returns a new worker object which lives in a new thread.
Definition: worker.h:201
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.
Definition: aircraftparts.h:26
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.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
Specialized class for distance units (meter, foot, nautical miles).
Definition: units.h:95
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.
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.
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
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.
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