Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/API/QGCCorePlugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,19 @@ QGCCorePlugin *QGCCorePlugin::instance()

const QVariantList &QGCCorePlugin::analyzePages()
{
// Log Viewer is excluded on mobile (Android/iOS) because parsing large log files
// (e.g. 900 MB ULog files with 1000+ fields) exhausts the mobile heap, causing
// OOM crashes. Proper mobile support requires time-bucketed downsampling and will
// be addressed in a future major release.
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
static const QVariantList analyzeList = {
#else
static const QVariantList analyzeList = {
QVariant::fromValue(new QmlComponentInfo(
tr("Log Viewer"),
QUrl::fromUserInput(QStringLiteral("qrc:/qml/QGroundControl/AnalyzeView/LogViewer/LogViewerPage.qml")),
QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/MAVLinkInspector.svg")))),
#endif
QVariant::fromValue(new QmlComponentInfo(
tr("Onboard Logs"),
QUrl::fromUserInput(QStringLiteral("qrc:/qml/QGroundControl/AnalyzeView/OnboardLogs/OnboardLogPage.qml")),
Expand Down
16 changes: 8 additions & 8 deletions src/AnalyzeView/LogViewer/APMDataFlash/APMDataFlashLogParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,8 @@ bool APMDataFlashLogParser::parseFile(const QString &filePath)
}
emit sampleCountChanged();

_parsed = true;
emit parsedChanged();
_parseComplete = true;
emit parseCompleteChanged();
qCDebug(APMDataFlashLogParserLog) << "Parsed fields" << _availableFields.count() << "parameters" << _parameters.count() << "events" << _events.count();
return true;
}
Expand Down Expand Up @@ -649,8 +649,8 @@ void APMDataFlashLogParser::parseFileAsync(const QString &filePath)
}
emit sampleCountChanged();

_parsed = true;
emit parsedChanged();
_parseComplete = true;
emit parseCompleteChanged();
emit parseFileFinished(filePath, true, QString());
});

Expand All @@ -661,10 +661,10 @@ void APMDataFlashLogParser::parseFileAsync(const QString &filePath)

void APMDataFlashLogParser::clear()
{
const bool oldParsed = _parsed;
_parsed = false;
if (oldParsed) {
emit parsedChanged();
const bool oldParseComplete = _parseComplete;
_parseComplete = false;
if (oldParseComplete) {
emit parseCompleteChanged();
}

if (!_parseError.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class APMDataFlashLogParser : public QObject
Q_OBJECT
QML_ELEMENT

Q_PROPERTY(bool parsed READ parsed NOTIFY parsedChanged)
Q_PROPERTY(bool parseComplete READ parseComplete NOTIFY parseCompleteChanged)
Q_PROPERTY(QString parseError READ parseError NOTIFY parseErrorChanged)
Q_PROPERTY(QStringList availableFields READ availableFields NOTIFY availableFieldsChanged)
Q_PROPERTY(QVariantList parameters READ parameters NOTIFY parametersChanged)
Expand All @@ -33,7 +33,7 @@ class APMDataFlashLogParser : public QObject
explicit APMDataFlashLogParser(QObject *parent = nullptr);
~APMDataFlashLogParser();

bool parsed() const { return _parsed; }
bool parseComplete() const { return _parseComplete; }
QString parseError() const { return _parseError; }
QStringList availableFields() const { return _availableFields; }
QVariantList parameters() const { return _parameters; }
Expand All @@ -55,7 +55,7 @@ class APMDataFlashLogParser : public QObject
Q_INVOKABLE QVariantList eventsNear(double timestampSeconds, double thresholdSeconds) const;

signals:
void parsedChanged();
void parseCompleteChanged();
void parseErrorChanged();
void availableFieldsChanged();
void parametersChanged();
Expand All @@ -71,7 +71,7 @@ class APMDataFlashLogParser : public QObject
private:
void _setParseError(const QString &error);

bool _parsed = false;
bool _parseComplete = false;
QString _parseError;
QStringList _availableFields;
QStringList _plottableFields;
Expand Down
43 changes: 41 additions & 2 deletions src/AnalyzeView/LogViewer/APMDataFlash/LogViewerDataFlashParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,38 @@

#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QFile>
#include <QtCore/QHash>
#include <QtCore/QRegularExpression>
#include <QtCore/QSet>
#include <QtCore/QTimeZone>
#include <QtCore/QVariantMap>

#include <algorithm>
#include <limits>

namespace {

int _leapSecondsTAI(int year, int month)
{
const int yyyymm = year * 100 + month;
if (yyyymm >= 201701) return 37;
if (yyyymm >= 201507) return 36;
if (yyyymm >= 201207) return 35;
if (yyyymm >= 200901) return 34;
if (yyyymm >= 200601) return 33;
if (yyyymm >= 199901) return 32;
if (yyyymm >= 199707) return 31;
if (yyyymm >= 199601) return 30;
return 0;
}

int _leapSecondsGPS(int year, int month)
{
return _leapSecondsTAI(year, month) - 19;
}

QString _vehicleTypeFromMessageText(const QString &messageText)
{
const QString text = messageText.toLower();
Expand Down Expand Up @@ -216,7 +237,7 @@ void _appendEvent(QVariantList &events, double timestampSecs, const QString &typ

namespace DataFlashParser {

LogParseResult parseFile(const QString &filePath)
LogParseResult parseFile(const QString &filePath, const ProgressCallback &progressCallback)
{
LogParseResult result;
result.sourceType = LogParseResult::SourceType::APMDataFlash;
Expand Down Expand Up @@ -291,6 +312,8 @@ LogParseResult parseFile(const QString &filePath)
static const QString kMODE = QStringLiteral("MODE");
static const QString kERR = QStringLiteral("ERR");
static const QString kEV = QStringLiteral("EV");
static const QString kGPS = QStringLiteral("GPS");
static const QString kGPS2 = QStringLiteral("GPS2");

APMDataFlashUtility::iterateMessages(bytes.constData(), bytes.size(), formats,
[&](uint8_t msgType, const char *payload, int, const APMDataFlashUtility::MessageFormat &fmt) {
Expand All @@ -301,6 +324,22 @@ LogParseResult parseFile(const QString &filePath)
maxTimestampSecs = std::max(maxTimestampSecs, timestampSecs);
}

if ((fmt.name == kGPS || fmt.name == kGPS2) && result.startTime.isNull()
&& values.contains(QStringLiteral("GWk")) && values.contains(QStringLiteral("GMS"))
&& timestampSecs >= 0.0) {
const int gwk = values.value(QStringLiteral("GWk")).toInt();
const int gms = values.value(QStringLiteral("GMS")).toInt();
if (gwk > 2000) {
const double gpsSecs = 315964800.0 + (7.0 * 24 * 60 * 60) * gwk + (gms / 1000.0);
const QDateTime gpsDateTime = QDateTime::fromMSecsSinceEpoch(
static_cast<qint64>(gpsSecs * 1000.0), QTimeZone::utc());
const int leapSecs = _leapSecondsGPS(gpsDateTime.date().year(), gpsDateTime.date().month());
const double utcSecs = gpsSecs - leapSecs;
result.startTime = QDateTime::fromMSecsSinceEpoch(
static_cast<qint64>((utcSecs - timestampSecs) * 1000.0), QTimeZone::utc());
}
}

if (fmt.name == kPARM) {
const QString paramName = values.value(QStringLiteral("Name")).toString();
const QVariant paramValue = values.contains(QStringLiteral("Value"))
Expand Down Expand Up @@ -386,7 +425,7 @@ LogParseResult parseFile(const QString &filePath)
}
}
return true;
});
}, progressCallback);

if (hasOpenModeSegment && (maxTimestampSecs >= modeSegmentStartSecs)) {
QVariantMap segment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
// Returns a filled LogParseResult on success (result.ok == true) or an error
// message in result.errorMessage on failure.
namespace DataFlashParser {
LogParseResult parseFile(const QString &filePath);
LogParseResult parseFile(const QString &filePath, const ProgressCallback &progressCallback = nullptr);
}
48 changes: 38 additions & 10 deletions src/AnalyzeView/LogViewer/LogFileParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ QGC_LOGGING_CATEGORY(LogFileParserLog, "AnalyzeView.LogFileParser")

namespace {

LogParseResult _parseFile(const QString &filePath)
LogParseResult _parseFile(const QString &filePath, const ProgressCallback &progressCallback = nullptr)
{
const QString suffix = QFileInfo(filePath).suffix().toLower();

if (suffix == QStringLiteral("bin") || suffix == QStringLiteral("log")) {
return DataFlashParser::parseFile(filePath);
return DataFlashParser::parseFile(filePath, progressCallback);
}

if (suffix == QStringLiteral("ulg")) {
return ULogParser::parseFile(filePath);
return ULogParser::parseFile(filePath, progressCallback);
}

const QString fileTypeDescription = suffix.isEmpty()
Expand Down Expand Up @@ -74,11 +74,28 @@ bool LogFileParser::parseFile(const QString &filePath)
return true;
}

void LogFileParser::startParsingAsync(const QString &filePath)
{
_parsing = true;
emit parsingChanged();
_parseProgress = 0.f;
emit parseProgressChanged();
parseFileAsync(filePath);
}

void LogFileParser::parseFileAsync(const QString &filePath)
{
const quint64 requestId = ++_parseRequestId;
clear();

auto progressCallback = [this, requestId](float v) {
QMetaObject::invokeMethod(this, [this, requestId, v]() {
if (requestId != _parseRequestId) return;
_parseProgress = v;
emit parseProgressChanged();
}, Qt::QueuedConnection);
};

auto *watcher = new QFutureWatcher<LogParseResult>(this);
(void) connect(watcher, &QFutureWatcher<LogParseResult>::finished, this,
[this, watcher, filePath, requestId]() {
Expand All @@ -89,6 +106,11 @@ void LogFileParser::parseFileAsync(const QString &filePath)
return;
}

_parseProgress = 1.f;
emit parseProgressChanged();
_parsing = false;
emit parsingChanged();

if (!result.ok) {
_setParseError(result.errorMessage);
emit parseFileFinished(filePath, false, result.errorMessage);
Expand All @@ -99,8 +121,8 @@ void LogFileParser::parseFileAsync(const QString &filePath)
emit parseFileFinished(filePath, true, QString());
});

watcher->setFuture(QtConcurrent::run([filePath]() {
return _parseFile(filePath);
watcher->setFuture(QtConcurrent::run([filePath, progressCallback]() {
return _parseFile(filePath, progressCallback);
}));
}

Expand Down Expand Up @@ -146,16 +168,20 @@ void LogFileParser::_applyResult(const LogParseResult &result)
emit timeRangeChanged();
}
emit sampleCountChanged();
if (_startTime != result.startTime) {
_startTime = result.startTime;
emit startTimeChanged();
}

_parsed = true;
emit parsedChanged();
_parseComplete = true;
emit parseCompleteChanged();
}

void LogFileParser::clear()
{
const bool oldParsed = _parsed;
_parsed = false;
if (oldParsed) { emit parsedChanged(); }
const bool oldParseComplete = _parseComplete;
_parseComplete = false;
if (oldParseComplete) { emit parseCompleteChanged(); }

if (!_parseError.isEmpty()) { _parseError.clear(); emit parseErrorChanged(); }
if (!_availableFields.isEmpty()) { _availableFields.clear(); emit availableFieldsChanged(); }
Expand All @@ -177,6 +203,8 @@ void LogFileParser::clear()
emit timeRangeChanged();
}
if (_sampleCount != 0) { _sampleCount = 0; emit sampleCountChanged(); }
if (!_startTime.isNull()) { _startTime = QDateTime(); emit startTimeChanged(); }
if (_parseProgress != 0.f) { _parseProgress = 0.f; emit parseProgressChanged(); }
}

QVariantList LogFileParser::fieldSamples(const QString &fieldName) const
Expand Down
22 changes: 18 additions & 4 deletions src/AnalyzeView/LogViewer/LogFileParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtCore/QVariantList>
#include <QtCore/QDateTime>
#include <QtCore/QVector>
#include <QtCore/QtGlobal>
#include <QtQmlIntegration/QtQmlIntegration>
Expand All @@ -30,7 +31,9 @@ class LogFileParser : public QObject
Q_OBJECT
QML_ELEMENT

Q_PROPERTY(bool parsed READ parsed NOTIFY parsedChanged)
Q_PROPERTY(bool parsing READ parsing NOTIFY parsingChanged)
Q_PROPERTY(float parseProgress READ parseProgress NOTIFY parseProgressChanged)
Q_PROPERTY(bool parseComplete READ parseComplete NOTIFY parseCompleteChanged)
Q_PROPERTY(QString parseError READ parseError NOTIFY parseErrorChanged)
Q_PROPERTY(QStringList availableFields READ availableFields NOTIFY availableFieldsChanged)
Q_PROPERTY(QVariantList parameters READ parameters NOTIFY parametersChanged)
Expand All @@ -43,12 +46,13 @@ class LogFileParser : public QObject
Q_PROPERTY(double minTimestamp READ minTimestamp NOTIFY timeRangeChanged)
Q_PROPERTY(double maxTimestamp READ maxTimestamp NOTIFY timeRangeChanged)
Q_PROPERTY(int sampleCount READ sampleCount NOTIFY sampleCountChanged)
Q_PROPERTY(QDateTime startTime READ startTime NOTIFY startTimeChanged)

public:
explicit LogFileParser(QObject *parent = nullptr);
~LogFileParser();

bool parsed() const { return _parsed; }
bool parseComplete() const { return _parseComplete; }
QString parseError() const { return _parseError; }
QStringList availableFields() const { return _availableFields; }
QVariantList parameters() const { return _parameters; }
Expand All @@ -61,9 +65,13 @@ class LogFileParser : public QObject
double minTimestamp() const { return _minTimestamp; }
double maxTimestamp() const { return _maxTimestamp; }
int sampleCount() const { return _sampleCount; }
QDateTime startTime() const { return _startTime; }
bool parsing() const { return _parsing; }
float parseProgress() const { return _parseProgress; }

Q_INVOKABLE bool parseFile(const QString &filePath);
Q_INVOKABLE void parseFileAsync(const QString &filePath);
Q_INVOKABLE void startParsingAsync(const QString &filePath);
Q_INVOKABLE void clear();
Q_INVOKABLE QVariantList fieldSamples(const QString &fieldName) const;
Q_INVOKABLE QVariantList fieldSamplesFiltered(const QString &fieldName, double minX, double maxX, int pixelWidth) const;
Expand All @@ -88,7 +96,7 @@ class LogFileParser : public QObject
Q_INVOKABLE QVariantMap gpsCoordAt(double timestampSeconds) const;

signals:
void parsedChanged();
void parseCompleteChanged();
void parseErrorChanged();
void availableFieldsChanged();
void parametersChanged();
Expand All @@ -100,13 +108,16 @@ class LogFileParser : public QObject
void detectedVehicleTypeChanged();
void timeRangeChanged();
void sampleCountChanged();
void startTimeChanged();
void parsingChanged();
void parseProgressChanged();
void parseFileFinished(const QString &filePath, bool ok, const QString &errorMessage);

private:
void _setParseError(const QString &error);
void _applyResult(const struct LogParseResult &result);

bool _parsed = false;
bool _parseComplete = false;
QString _parseError;
QStringList _availableFields;
QStringList _plottableFields;
Expand All @@ -121,6 +132,9 @@ class LogFileParser : public QObject
double _maxTimestamp = -1.0;
int _sampleCount = 0;
quint64 _parseRequestId = 0;
QDateTime _startTime;
bool _parsing = false;
float _parseProgress = 0.f;

// Cached GPS field names, set by gpsPath() when a valid candidate is found.
mutable QString _gpsLatField;
Expand Down
Loading
Loading