5#include <QtCore/QByteArray>
6#include <QtCore/QCoreApplication>
7#include <QtCore/QDateTime>
10#include <QtCore/QRegularExpression>
12#include <QtCore/QTimeZone>
13#include <QtCore/QVariantMap>
20int _leapSecondsTAI(
int year,
int month)
22 const int yyyymm = year * 100 + month;
23 if (yyyymm >= 201701)
return 37;
24 if (yyyymm >= 201507)
return 36;
25 if (yyyymm >= 201207)
return 35;
26 if (yyyymm >= 200901)
return 34;
27 if (yyyymm >= 200601)
return 33;
28 if (yyyymm >= 199901)
return 32;
29 if (yyyymm >= 199707)
return 31;
30 if (yyyymm >= 199601)
return 30;
34int _leapSecondsGPS(
int year,
int month)
36 return _leapSecondsTAI(year, month) - 19;
39QString _vehicleTypeFromMessageText(
const QString &messageText)
41 const QString text = messageText.toLower();
42 if (text.contains(QStringLiteral(
"arducopter"))) {
return QStringLiteral(
"ArduCopter"); }
43 if (text.contains(QStringLiteral(
"arduplane"))) {
return QStringLiteral(
"ArduPlane"); }
44 if (text.contains(QStringLiteral(
"ardurover"))) {
return QStringLiteral(
"ArduRover"); }
45 if (text.contains(QStringLiteral(
"ardusub"))) {
return QStringLiteral(
"ArduSub"); }
50void _parseFirmwareVersionFromMessageText(
const QString &messageText,
int &major,
int &minor)
52 static const QRegularExpression re(QStringLiteral(
"V(\\d+)\\.(\\d+)"));
53 const QRegularExpressionMatch m = re.match(messageText);
55 major = m.captured(1).toInt();
56 minor = m.captured(2).toInt();
60QString _ardupilotModeName(
const QString &vehicleType,
int modeNumber)
62 static const QHash<int, QString> planeModes = {
63 {0,QStringLiteral(
"Manual")},{1,QStringLiteral(
"CIRCLE")},{2,QStringLiteral(
"STABILIZE")},
64 {3,QStringLiteral(
"TRAINING")},{4,QStringLiteral(
"ACRO")},{5,QStringLiteral(
"FBWA")},
65 {6,QStringLiteral(
"FBWB")},{7,QStringLiteral(
"CRUISE")},{8,QStringLiteral(
"AUTOTUNE")},
66 {10,QStringLiteral(
"Auto")},{11,QStringLiteral(
"RTL")},{12,QStringLiteral(
"Loiter")},
67 {13,QStringLiteral(
"TAKEOFF")},{14,QStringLiteral(
"AVOID_ADSB")},{15,QStringLiteral(
"Guided")},
68 {17,QStringLiteral(
"QSTABILIZE")},{18,QStringLiteral(
"QHOVER")},{19,QStringLiteral(
"QLOITER")},
69 {20,QStringLiteral(
"QLAND")},{21,QStringLiteral(
"QRTL")},{22,QStringLiteral(
"QAUTOTUNE")},
70 {23,QStringLiteral(
"QACRO")},{24,QStringLiteral(
"THERMAL")},{25,QStringLiteral(
"Loiter to QLand")},
71 {26,QStringLiteral(
"AUTOLAND")},
73 static const QHash<int, QString> copterModes = {
74 {0,QStringLiteral(
"Stabilize")},{1,QStringLiteral(
"Acro")},{2,QStringLiteral(
"AltHold")},
75 {3,QStringLiteral(
"Auto")},{4,QStringLiteral(
"Guided")},{5,QStringLiteral(
"Loiter")},
76 {6,QStringLiteral(
"RTL")},{7,QStringLiteral(
"Circle")},{9,QStringLiteral(
"Land")},
77 {11,QStringLiteral(
"Drift")},{13,QStringLiteral(
"Sport")},{14,QStringLiteral(
"Flip")},
78 {15,QStringLiteral(
"AutoTune")},{16,QStringLiteral(
"PosHold")},{17,QStringLiteral(
"Brake")},
79 {18,QStringLiteral(
"Throw")},{19,QStringLiteral(
"Avoid_ADSB")},{20,QStringLiteral(
"Guided_NoGPS")},
80 {21,QStringLiteral(
"Smart_RTL")},{22,QStringLiteral(
"FlowHold")},{23,QStringLiteral(
"Follow")},
81 {24,QStringLiteral(
"ZigZag")},{25,QStringLiteral(
"SystemID")},{26,QStringLiteral(
"Heli_Autorotate")},
82 {27,QStringLiteral(
"Auto RTL")},{28,QStringLiteral(
"Turtle")},
84 if (vehicleType == QStringLiteral(
"ArduPlane")) {
85 return planeModes.value(modeNumber, QStringLiteral(
"Mode %1").arg(modeNumber));
87 if (vehicleType == QStringLiteral(
"ArduCopter")) {
88 return copterModes.value(modeNumber, QStringLiteral(
"Mode %1").arg(modeNumber));
90 return QStringLiteral(
"Mode %1").arg(modeNumber);
93QString _ardupilotErrDescription(
int subsystem,
int ecode)
97 case 1: sub = QCoreApplication::translate(
"LogFileParser",
"Main");
break;
98 case 2: sub = QCoreApplication::translate(
"LogFileParser",
"Radio");
break;
99 case 3: sub = QCoreApplication::translate(
"LogFileParser",
"Compass");
break;
100 case 4: sub = QCoreApplication::translate(
"LogFileParser",
"Optflow");
break;
101 case 5: sub = QCoreApplication::translate(
"LogFileParser",
"Radio Failsafe");
break;
102 case 6: sub = QCoreApplication::translate(
"LogFileParser",
"Battery Failsafe");
break;
103 case 7: sub = QCoreApplication::translate(
"LogFileParser",
"GPS Failsafe");
break;
104 case 8: sub = QCoreApplication::translate(
"LogFileParser",
"GCS Failsafe");
break;
105 case 9: sub = QCoreApplication::translate(
"LogFileParser",
"Fence Failsafe");
break;
106 case 10: sub = QCoreApplication::translate(
"LogFileParser",
"Flight mode");
break;
107 case 11: sub = QCoreApplication::translate(
"LogFileParser",
"GPS");
break;
108 case 12: sub = QCoreApplication::translate(
"LogFileParser",
"Crash Check");
break;
109 case 13: sub = QCoreApplication::translate(
"LogFileParser",
"Flip");
break;
110 case 14: sub = QCoreApplication::translate(
"LogFileParser",
"Autotune");
break;
111 case 15: sub = QCoreApplication::translate(
"LogFileParser",
"Parachute");
break;
112 case 16: sub = QCoreApplication::translate(
"LogFileParser",
"EKF Check");
break;
113 case 17: sub = QCoreApplication::translate(
"LogFileParser",
"EKF Failsafe");
break;
114 case 18: sub = QCoreApplication::translate(
"LogFileParser",
"Barometer");
break;
115 case 19: sub = QCoreApplication::translate(
"LogFileParser",
"CPU Load Watchdog");
break;
116 case 20: sub = QCoreApplication::translate(
"LogFileParser",
"ADSB Failsafe");
break;
117 case 21: sub = QCoreApplication::translate(
"LogFileParser",
"Terrain Data");
break;
118 case 22: sub = QCoreApplication::translate(
"LogFileParser",
"Navigation");
break;
119 case 23: sub = QCoreApplication::translate(
"LogFileParser",
"Terrain Failsafe");
break;
120 case 24: sub = QCoreApplication::translate(
"LogFileParser",
"EKF Primary");
break;
121 case 25: sub = QCoreApplication::translate(
"LogFileParser",
"Thrust Loss Check");
break;
122 case 26: sub = QCoreApplication::translate(
"LogFileParser",
"Sensor Failsafe");
break;
123 case 27: sub = QCoreApplication::translate(
"LogFileParser",
"Leak Failsafe");
break;
124 case 28: sub = QCoreApplication::translate(
"LogFileParser",
"Pilot Input");
break;
125 case 29: sub = QCoreApplication::translate(
"LogFileParser",
"Vibration Failsafe");
break;
126 case 30: sub = QCoreApplication::translate(
"LogFileParser",
"Internal Error");
break;
127 case 31: sub = QCoreApplication::translate(
"LogFileParser",
"Deadreckon Failsafe");
break;
128 default: sub = QCoreApplication::translate(
"LogFileParser",
"Subsystem %1").arg(subsystem);
break;
130 return QStringLiteral(
"%1 (%2): Code %3").arg(sub).arg(subsystem).arg(ecode);
133QString _ardupilotEventDescription(
int eventId)
136 case 10:
return QCoreApplication::translate(
"LogFileParser",
"Armed");
137 case 11:
return QCoreApplication::translate(
"LogFileParser",
"Disarmed");
138 case 15:
return QCoreApplication::translate(
"LogFileParser",
"Auto Armed");
139 case 17:
return QCoreApplication::translate(
"LogFileParser",
"Land Complete Maybe");
140 case 18:
return QCoreApplication::translate(
"LogFileParser",
"Land Complete");
141 case 19:
return QCoreApplication::translate(
"LogFileParser",
"Lost GPS");
142 case 21:
return QCoreApplication::translate(
"LogFileParser",
"Flip Start");
143 case 22:
return QCoreApplication::translate(
"LogFileParser",
"Flip End");
144 case 25:
return QCoreApplication::translate(
"LogFileParser",
"Set Home");
145 case 26:
return QCoreApplication::translate(
"LogFileParser",
"Simple Mode Enabled");
146 case 27:
return QCoreApplication::translate(
"LogFileParser",
"Simple Mode Disabled");
147 case 28:
return QCoreApplication::translate(
"LogFileParser",
"Not Landed");
148 case 29:
return QCoreApplication::translate(
"LogFileParser",
"Super Simple Mode Enabled");
149 case 30:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Initialised");
150 case 31:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Off");
151 case 32:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Restart");
152 case 33:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Success");
153 case 34:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Failed");
154 case 35:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Reached Limit");
155 case 36:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Pilot Testing");
156 case 37:
return QCoreApplication::translate(
"LogFileParser",
"AutoTune Saved Gains");
157 case 38:
return QCoreApplication::translate(
"LogFileParser",
"Save Trim");
158 case 39:
return QCoreApplication::translate(
"LogFileParser",
"Save Waypoint Add");
159 case 41:
return QCoreApplication::translate(
"LogFileParser",
"Fence Enabled");
160 case 42:
return QCoreApplication::translate(
"LogFileParser",
"Fence Disabled");
161 case 43:
return QCoreApplication::translate(
"LogFileParser",
"Acro Trainer Off");
162 case 44:
return QCoreApplication::translate(
"LogFileParser",
"Acro Trainer Leveling");
163 case 45:
return QCoreApplication::translate(
"LogFileParser",
"Acro Trainer Limited");
164 case 46:
return QCoreApplication::translate(
"LogFileParser",
"Gripper Grab");
165 case 47:
return QCoreApplication::translate(
"LogFileParser",
"Gripper Release");
166 case 49:
return QCoreApplication::translate(
"LogFileParser",
"Parachute Disabled");
167 case 50:
return QCoreApplication::translate(
"LogFileParser",
"Parachute Enabled");
168 case 51:
return QCoreApplication::translate(
"LogFileParser",
"Parachute Released");
169 case 52:
return QCoreApplication::translate(
"LogFileParser",
"Landing Gear Deployed");
170 case 53:
return QCoreApplication::translate(
"LogFileParser",
"Landing Gear Retracted");
171 case 54:
return QCoreApplication::translate(
"LogFileParser",
"Motors Emergency Stopped");
172 case 55:
return QCoreApplication::translate(
"LogFileParser",
"Motors Emergency Stop Cleared");
173 case 56:
return QCoreApplication::translate(
"LogFileParser",
"Motors Interlock Disabled");
174 case 57:
return QCoreApplication::translate(
"LogFileParser",
"Motors Interlock Enabled");
175 case 58:
return QCoreApplication::translate(
"LogFileParser",
"Rotor Runup Complete");
176 case 59:
return QCoreApplication::translate(
"LogFileParser",
"Rotor Speed Below Critical");
177 case 60:
return QCoreApplication::translate(
"LogFileParser",
"EKF Altitude Reset");
178 case 61:
return QCoreApplication::translate(
"LogFileParser",
"Land Cancelled By Pilot");
179 case 62:
return QCoreApplication::translate(
"LogFileParser",
"EKF Yaw Reset");
180 case 63:
return QCoreApplication::translate(
"LogFileParser",
"ADSB Avoidance Enabled");
181 case 64:
return QCoreApplication::translate(
"LogFileParser",
"ADSB Avoidance Disabled");
182 case 65:
return QCoreApplication::translate(
"LogFileParser",
"Proximity Avoidance Enabled");
183 case 66:
return QCoreApplication::translate(
"LogFileParser",
"Proximity Avoidance Disabled");
184 case 67:
return QCoreApplication::translate(
"LogFileParser",
"GPS Primary Changed");
185 case 71:
return QCoreApplication::translate(
"LogFileParser",
"ZigZag Store A");
186 case 72:
return QCoreApplication::translate(
"LogFileParser",
"ZigZag Store B");
187 case 73:
return QCoreApplication::translate(
"LogFileParser",
"Land Repo Active");
188 case 74:
return QCoreApplication::translate(
"LogFileParser",
"Standby Enabled");
189 case 75:
return QCoreApplication::translate(
"LogFileParser",
"Standby Disabled");
190 case 76:
return QCoreApplication::translate(
"LogFileParser",
"Fence Alt Max Enabled");
191 case 77:
return QCoreApplication::translate(
"LogFileParser",
"Fence Alt Max Disabled");
192 case 78:
return QCoreApplication::translate(
"LogFileParser",
"Fence Circle Enabled");
193 case 79:
return QCoreApplication::translate(
"LogFileParser",
"Fence Circle Disabled");
194 case 80:
return QCoreApplication::translate(
"LogFileParser",
"Fence Alt Min Enabled");
195 case 81:
return QCoreApplication::translate(
"LogFileParser",
"Fence Alt Min Disabled");
196 case 82:
return QCoreApplication::translate(
"LogFileParser",
"Fence Polygon Enabled");
197 case 83:
return QCoreApplication::translate(
"LogFileParser",
"Fence Polygon Disabled");
198 case 85:
return QCoreApplication::translate(
"LogFileParser",
"EK3 Source Set: Primary");
199 case 86:
return QCoreApplication::translate(
"LogFileParser",
"EK3 Source Set: Secondary");
200 case 87:
return QCoreApplication::translate(
"LogFileParser",
"EK3 Source Set: Tertiary");
201 case 90:
return QCoreApplication::translate(
"LogFileParser",
"Airspeed Primary Changed");
202 case 163:
return QCoreApplication::translate(
"LogFileParser",
"Surfaced");
203 case 164:
return QCoreApplication::translate(
"LogFileParser",
"Not Surfaced");
204 case 165:
return QCoreApplication::translate(
"LogFileParser",
"Bottomed");
205 case 166:
return QCoreApplication::translate(
"LogFileParser",
"Not Bottomed");
206 default:
return QCoreApplication::translate(
"LogFileParser",
"Event %1").arg(eventId);
210double _extractTimestampSeconds(
const QMap<QString, QVariant> &values)
212 if (values.contains(QStringLiteral(
"TimeUS"))) {
213 return values.value(QStringLiteral(
"TimeUS")).toDouble() / 1000000.0;
215 if (values.contains(QStringLiteral(
"TimeMS"))) {
216 return values.value(QStringLiteral(
"TimeMS")).toDouble() / 1000.0;
218 if (values.contains(QStringLiteral(
"Time"))) {
219 return values.value(QStringLiteral(
"Time")).toDouble() / 1000.0;
224void _appendEvent(QVariantList &
events,
double timestampSecs,
const QString &type,
const QString &description)
226 if ((timestampSecs < 0.0) || description.isEmpty()) {
229 QVariantMap eventRow;
230 eventRow[QStringLiteral(
"time")] = timestampSecs;
231 eventRow[QStringLiteral(
"type")] = type;
232 eventRow[QStringLiteral(
"description")] = description;
245 QFile file(filePath);
246 if (!file.open(QIODevice::ReadOnly)) {
247 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"Failed to open file");
251 const qint64 fileSize = file.size();
253 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"File is empty");
256 if (fileSize > std::numeric_limits<qsizetype>::max()) {
257 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"File is too large to parse");
261 uchar *
const mappedData = file.map(0, fileSize);
262 if (mappedData ==
nullptr) {
263 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"Failed to memory-map file");
269 uchar *data =
nullptr;
270 ~ScopedUnmap() {
if (data) { file.unmap(data); } }
271 } scopedUnmap{file, mappedData};
273 const char *
const raw =
reinterpret_cast<const char *
>(mappedData);
277 static_cast<uint8_t
>(raw[0]) != 0xA3 ||
278 static_cast<uint8_t
>(raw[1]) != 0x95) {
279 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"File does not appear to be a DataFlash log (invalid header)");
283 const QByteArray bytes = QByteArray::fromRawData(raw,
static_cast<qsizetype
>(fileSize));
285 QMap<uint8_t, APMDataFlashUtility::MessageFormat> formats;
287 result.
errorMessage = QCoreApplication::translate(
"LogFileParser",
"No valid FMT messages were found");
291 QSet<QString> fieldSet;
292 QSet<QString> plottableFieldSet;
293 double minTimestampSecs = -1.0;
294 double maxTimestampSecs = -1.0;
295 bool hasOpenModeSegment =
false;
296 double modeSegmentStartSecs = -1.0;
297 QString currentModeName;
299 QHash<uint8_t, QHash<QString, QString>> fieldNameByCol;
300 fieldNameByCol.reserve(formats.size());
301 for (
auto fmtIt = formats.cbegin(); fmtIt != formats.cend(); ++fmtIt) {
303 QHash<QString, QString> &perCol = fieldNameByCol[fmtIt.key()];
304 perCol.reserve(fmt.
columns.size());
305 for (
const QString &col : fmt.
columns) {
306 perCol.insert(col, fmt.
name + QLatin1Char(
'.') + col);
310 static const QString kPARM = QStringLiteral(
"PARM");
311 static const QString kMSG = QStringLiteral(
"MSG");
312 static const QString kMODE = QStringLiteral(
"MODE");
313 static const QString kERR = QStringLiteral(
"ERR");
314 static const QString kEV = QStringLiteral(
"EV");
315 static const QString kGPS = QStringLiteral(
"GPS");
316 static const QString kGPS2 = QStringLiteral(
"GPS2");
320 const QMap<QString, QVariant> values = APMDataFlashUtility::parseMessage(payload, fmt);
321 const double timestampSecs = _extractTimestampSeconds(values);
322 if (timestampSecs >= 0.0) {
323 if (minTimestampSecs < 0.0 || timestampSecs < minTimestampSecs) { minTimestampSecs = timestampSecs; }
324 maxTimestampSecs = std::max(maxTimestampSecs, timestampSecs);
328 && values.contains(QStringLiteral(
"GWk")) && values.contains(QStringLiteral(
"GMS"))
329 && timestampSecs >= 0.0) {
330 const int gwk = values.value(QStringLiteral(
"GWk")).toInt();
331 const int gms = values.value(QStringLiteral(
"GMS")).toInt();
333 const double gpsSecs = 315964800.0 + (7.0 * 24 * 60 * 60) * gwk + (gms / 1000.0);
334 const QDateTime gpsDateTime = QDateTime::fromMSecsSinceEpoch(
335 static_cast<qint64
>(gpsSecs * 1000.0), QTimeZone::utc());
336 const int leapSecs = _leapSecondsGPS(gpsDateTime.date().year(), gpsDateTime.date().month());
337 const double utcSecs = gpsSecs - leapSecs;
338 result.
startTime = QDateTime::fromMSecsSinceEpoch(
339 static_cast<qint64
>((utcSecs - timestampSecs) * 1000.0), QTimeZone::utc());
343 if (fmt.
name == kPARM) {
344 const QString paramName = values.value(QStringLiteral(
"Name")).toString();
345 const QVariant paramValue = values.contains(QStringLiteral(
"Value"))
346 ? values.value(QStringLiteral(
"Value"))
347 : values.value(QStringLiteral(
"Val"));
348 if (!paramName.isEmpty()) {
350 row[QStringLiteral(
"name")] = paramName;
351 row[QStringLiteral(
"value")] = paramValue;
353 row[QStringLiteral(
"isFloat")] = paramValue.metaType() == QMetaType::fromType<float>()
354 || paramValue.metaType() == QMetaType::fromType<double>();
355 row[QStringLiteral(
"hasDefault")] =
false;
356 row[QStringLiteral(
"defaultValue")] = QVariant();
357 row[QStringLiteral(
"isDefault")] =
false;
360 }
else if (fmt.
name == kMSG) {
361 const QString text = values.value(QStringLiteral(
"Message")).toString();
362 const QString detected = _vehicleTypeFromMessageText(text);
367 if (!text.isEmpty()) {
369 row[QStringLiteral(
"time")] = timestampSecs;
370 row[QStringLiteral(
"text")] = text;
373 }
else if (fmt.
name == kMODE) {
374 QString modeName = values.value(QStringLiteral(
"Mode")).toString();
375 bool isNumeric =
false;
376 const int modeNumber = modeName.toInt(&isNumeric);
379 }
else if (modeName.isEmpty()) {
380 modeName = QCoreApplication::translate(
"LogFileParser",
"Unknown");
382 _appendEvent(result.
events, timestampSecs, QStringLiteral(
"mode"),
383 QCoreApplication::translate(
"LogFileParser",
"Mode: %1").arg(modeName));
384 if (timestampSecs >= 0.0) {
385 if (hasOpenModeSegment && (timestampSecs > modeSegmentStartSecs)) {
387 segment[QStringLiteral(
"mode")] = currentModeName;
388 segment[QStringLiteral(
"start")] = modeSegmentStartSecs;
389 segment[QStringLiteral(
"end")] = timestampSecs;
392 hasOpenModeSegment =
true;
393 modeSegmentStartSecs = timestampSecs;
394 currentModeName = modeName;
396 }
else if (fmt.
name == kERR) {
397 const int subsystem = values.value(QStringLiteral(
"Subsys")).toInt();
398 const int ecode = values.value(QStringLiteral(
"ECode")).toInt();
399 _appendEvent(result.
events, timestampSecs, QStringLiteral(
"error"),
400 _ardupilotErrDescription(subsystem, ecode));
401 }
else if (fmt.
name == kEV) {
402 const int eventId = values.value(QStringLiteral(
"Id"), values.value(QStringLiteral(
"Event"))).toInt();
403 _appendEvent(result.
events, timestampSecs, QStringLiteral(
"event"),
404 _ardupilotEventDescription(eventId));
409 const QHash<QString, QString> &perCol = fieldNameByCol[msgType];
410 const bool haveTimestamp = (timestampSecs >= 0.0);
411 for (
auto it = values.cbegin(); it != values.cend(); ++it) {
412 const auto nameIt = perCol.constFind(it.key());
413 if (nameIt == perCol.constEnd()) {
continue; }
414 const QString &fieldName = nameIt.value();
415 fieldSet.insert(fieldName);
416 if (!haveTimestamp) {
continue; }
417 const int typeId = it.value().metaType().id();
419 (typeId == QMetaType::Int) || (typeId == QMetaType::UInt) ||
420 (typeId == QMetaType::LongLong) || (typeId == QMetaType::ULongLong) ||
421 (typeId == QMetaType::Float) || (typeId == QMetaType::Double);
423 result.
fieldSamples[fieldName].append(QPointF(timestampSecs, it.value().toDouble()));
424 plottableFieldSet.insert(fieldName);
427 return !cancelToken || !cancelToken->load(std::memory_order_relaxed);
428 }, progressCallback);
430 if (cancelToken && cancelToken->load(std::memory_order_relaxed)) {
434 if (hasOpenModeSegment && (maxTimestampSecs >= modeSegmentStartSecs)) {
436 segment[QStringLiteral(
"mode")] = currentModeName;
437 segment[QStringLiteral(
"start")] = modeSegmentStartSecs;
438 segment[QStringLiteral(
"end")] = maxTimestampSecs;
439 result.modeSegments.append(segment);
442 result.availableFields = fieldSet.values();
443 std::sort(result.availableFields.begin(), result.availableFields.end());
444 result.plottableFields = plottableFieldSet.values();
445 std::sort(result.plottableFields.begin(), result.plottableFields.end());
446 result.minTimestamp = minTimestampSecs;
447 result.maxTimestamp = maxTimestampSecs;
std::function< void(float)> ProgressCallback
std::shared_ptr< std::atomic< bool > > CancelToken
bool parseFmtMessages(const char *data, qint64 size, QMap< uint8_t, MessageFormat > &formats)
int iterateMessages(const char *data, qint64 size, const QMap< uint8_t, MessageFormat > &formats, const MessageCallback &callback, const std::function< void(float)> &progressCallback)
LogParseResult parseFile(const QString &filePath, const ProgressCallback &progressCallback, const CancelToken &cancelToken)
QString detectedVehicleType
QVariantList modeSegments
QHash< QString, QVector< QPointF > > fieldSamples