Skip to content

Commit

Permalink
AnalyzeView: Download Logs Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey committed Dec 15, 2024
1 parent feaf7c5 commit d0f173a
Show file tree
Hide file tree
Showing 10 changed files with 694 additions and 713 deletions.
856 changes: 436 additions & 420 deletions src/AnalyzeView/LogDownloadController.cc

Large diffs are not rendered by default.

115 changes: 61 additions & 54 deletions src/AnalyzeView/LogDownloadController.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,81 +9,88 @@

#pragma once

#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QLoggingCategory>
#include <QtCore/QObject>
#include <QtQmlIntegration/QtQmlIntegration>

#include "QmlObjectListModel.h"

Q_DECLARE_LOGGING_CATEGORY(LogDownloadControllerLog)

class Vehicle;
class QGCLogEntry;
struct LogDownloadData;
class QGCLogEntry;
class QmlObjectListModel;
class QTimer;
class QThread;
class Vehicle;
class LogDownloadTest;

//-----------------------------------------------------------------------------
class LogDownloadController : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_MOC_INCLUDE("Vehicle.h")
Q_MOC_INCLUDE("QmlObjectListModel.h")
Q_PROPERTY(QmlObjectListModel *model READ _getModel CONSTANT)
Q_PROPERTY(bool requestingList READ _getRequestingList NOTIFY requestingListChanged)
Q_PROPERTY(bool downloadingLogs READ _getDownloadingLogs NOTIFY downloadingLogsChanged)

public:
LogDownloadController(void);

Q_PROPERTY(QmlObjectListModel* model READ model NOTIFY modelChanged)
Q_PROPERTY(bool requestingList READ requestingList NOTIFY requestingListChanged)
Q_PROPERTY(bool downloadingLogs READ downloadingLogs NOTIFY downloadingLogsChanged)
friend class LogDownloadTest;

QmlObjectListModel* model () { return &_logEntriesModel; }
bool requestingList () const{ return _requestingLogEntries; }
bool downloadingLogs () const{ return _downloadingLogs; }

Q_INVOKABLE void refresh ();
Q_INVOKABLE void download (QString path = QString());
Q_INVOKABLE void eraseAll ();
Q_INVOKABLE void cancel ();
public:
explicit LogDownloadController(QObject *parent = nullptr);
~LogDownloadController();

void downloadToDirectory(const QString& dir);
Q_INVOKABLE void refresh();
Q_INVOKABLE void download(const QString &path = QString());
Q_INVOKABLE void eraseAll();
Q_INVOKABLE void cancel();

signals:
void requestingListChanged ();
void downloadingLogsChanged ();
void modelChanged ();
void selectionChanged ();
void requestingListChanged();
void downloadingLogsChanged();
void selectionChanged();

private slots:
void _setActiveVehicle (Vehicle* vehicle);
void _logEntry (uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num);
void _logData (uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data);
void _processDownload ();
void _setActiveVehicle(Vehicle *vehicle);
void _logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num);
void _logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data);
void _processDownload();

private:
bool _entriesComplete ();
bool _chunkComplete () const;
bool _logComplete () const;
QmlObjectListModel *_getModel() const { return _logEntriesModel; }
bool _getRequestingList() const { return _requestingLogEntries; }
bool _getDownloadingLogs() const { return _downloadingLogs; }

bool _chunkComplete() const;
bool _entriesComplete() const;
bool _logComplete() const;
bool _prepareLogDownload();
void _downloadToDirectory(const QString &dir);
void _findMissingData();
void _findMissingEntries();
void _receivedAllData();
void _receivedAllEntries();
void _receivedAllData ();
void _resetSelection (bool canceled = false);
void _findMissingData ();
void _requestLogList (uint32_t start, uint32_t end);
void _requestLogData (uint16_t id, uint32_t offset, uint32_t count, int retryCount = 0);
bool _prepareLogDownload();
void _setDownloading (bool active);
void _setListing (bool active);
void _updateDataRate ();

QGCLogEntry* _getNextSelected();

LogDownloadData* _downloadData;
QTimer _timer;
QmlObjectListModel _logEntriesModel;
Vehicle* _vehicle;
bool _requestingLogEntries;
bool _downloadingLogs;
int _retries;
int _apmOneBased;
QString _downloadPath;
void _requestLogData(uint16_t id, uint32_t offset, uint32_t count, int retryCount = 0);
void _requestLogList(uint32_t start, uint32_t end);
void _requestLogEnd();
void _resetSelection(bool canceled = false);
void _setDownloading(bool active);
void _setListing(bool active);
void _updateDataRate();

QGCLogEntry *_getNextSelected() const;

QTimer *_timer = nullptr;
QmlObjectListModel *_logEntriesModel = nullptr;

bool _downloadingLogs = false;
bool _requestingLogEntries = false;
int _apmOffset = 0;
int _retries = 0;
std::unique_ptr<LogDownloadData> _downloadData;
QString _downloadPath;
Vehicle *_vehicle = nullptr;

static constexpr uint32_t kTimeOutMs = 500;
static constexpr uint32_t kGUIRateMs = 17; ///< 1000ms / 60fps
static constexpr uint32_t kRequestLogListTimeoutMs = 5000;
};
136 changes: 62 additions & 74 deletions src/AnalyzeView/LogDownloadPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,43 @@

import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import Qt.labs.qmlmodels

import QGroundControl
import QGroundControl.Palette
import QGroundControl.Controls
import QGroundControl.Controllers
import QGroundControl.ScreenTools

AnalyzePage {
id: logDownloadPage
pageComponent: pageComponent
pageDescription: qsTr("Log Download allows you to download binary log files from your vehicle. Click Refresh to get list of available logs.")

property real _margin: ScreenTools.defaultFontPixelWidth

QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
id: logDownloadPage
pageComponent: pageComponent
pageDescription: qsTr("Log Download allows you to download binary log files from your vehicle. Click Refresh to get list of available logs.")

Component {
id: pageComponent

RowLayout {
width: availableWidth
width: availableWidth
height: availableHeight

QGCFlickable {
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: gridLayout.width
contentHeight: gridLayout.height
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: gridLayout.width
contentHeight: gridLayout.height

GridLayout {
id: gridLayout
rows: logController.model.count + 1
columns: 5
flow: GridLayout.TopToBottom
columnSpacing: ScreenTools.defaultFontPixelWidth
rowSpacing: 0
id: gridLayout
rows: logController.model.count + 1
columns: 5
flow: GridLayout.TopToBottom
columnSpacing: ScreenTools.defaultFontPixelWidth
rowSpacing: 0

QGCCheckBox {
id: headerCheckBox
enabled: false
id: headerCheckBox
enabled: false
}

Repeater {
Expand All @@ -66,52 +60,43 @@ AnalyzePage {
}
}

QGCLabel {
text: qsTr("Id")
}
QGCLabel { text: qsTr("Id") }

Repeater {
model: logController.model

QGCLabel {
text: object.id
}
QGCLabel { text: object.id }
}

QGCLabel {
text: qsTr("Date")
}
QGCLabel { text: qsTr("Date") }

Repeater {
model: logController.model

QGCLabel {
text: {
if (object.received) {
var d = object.time
if (d.getUTCFullYear() < 2010)
return qsTr("Date Unknown")
else
return d.toLocaleString(undefined)
if (!object.received) {
return ""
}

if (object.time.getUTCFullYear() < 2010) {
return qsTr("Date Unknown")
}
return ""

return object.time.toLocaleString(undefined)
}
}
}

QGCLabel {
text: qsTr("Size")
}
QGCLabel { text: qsTr("Size") }

Repeater {
model: logController.model

QGCLabel { text: object.sizeStr }
}

QGCLabel {
text: qsTr("Status")
}
QGCLabel { text: qsTr("Status") }

Repeater {
model: logController.model
Expand All @@ -122,52 +107,53 @@ AnalyzePage {
}

ColumnLayout {
spacing: _margin
Layout.alignment: Qt.AlignTop
Layout.fillWidth: false
spacing: ScreenTools.defaultFontPixelWidth
Layout.alignment: Qt.AlignTop
Layout.fillWidth: false

QGCButton {
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs
text: qsTr("Refresh")
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs
text: qsTr("Refresh")

onClicked: {
if (!QGroundControl.multiVehicleManager.activeVehicle || QGroundControl.multiVehicleManager.activeVehicle.isOfflineEditingVehicle) {
mainWindow.showMessageDialog(qsTr("Log Refresh"), qsTr("You must be connected to a vehicle in order to download logs."))
} else {
logController.refresh()
return
}

logController.refresh()
}
}

QGCButton {
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs
text: qsTr("Download")
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs
text: qsTr("Download")

onClicked: {
var logsSelected = false
for (var i = 0; i < logController.model.count; i++) {
var o = logController.model.get(i)
if (o.selected) {
if (logController.model.get(i).selected) {
logsSelected = true
break
}
}

if (!logsSelected) {
mainWindow.showMessageDialog(qsTr("Log Download"), qsTr("You must select at least one log file to download."))
return
}

if (ScreenTools.isMobile) {
// You can't pick folders in mobile, only default location is used
logController.download()
} else {
fileDialog.title = qsTr("Select save directory")
fileDialog.folder = QGroundControl.settingsManager.appSettings.logSavePath
fileDialog.selectFolder = true
fileDialog.openForLoad()
return
}

fileDialog.title = qsTr("Select save directory")
fileDialog.folder = QGroundControl.settingsManager.appSettings.logSavePath
fileDialog.selectFolder = true
fileDialog.openForLoad()
}

QGCFileDialog {
Expand All @@ -180,20 +166,22 @@ AnalyzePage {
}

QGCButton {
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs && logController.model.count > 0
text: qsTr("Erase All")
onClicked: mainWindow.showMessageDialog(qsTr("Delete All Log Files"),
qsTr("All log files will be erased permanently. Is this really what you want?"),
Dialog.Yes | Dialog.No,
function() { logController.eraseAll() })
Layout.fillWidth: true
enabled: !logController.requestingList && !logController.downloadingLogs && (logController.model.count > 0)
text: qsTr("Erase All")
onClicked: mainWindow.showMessageDialog(
qsTr("Delete All Log Files"),
qsTr("All log files will be erased permanently. Is this really what you want?"),
Dialog.Yes | Dialog.No,
function() { logController.eraseAll() }
)
}

QGCButton {
Layout.fillWidth: true
text: qsTr("Cancel")
enabled: logController.requestingList || logController.downloadingLogs
onClicked: logController.cancel()
Layout.fillWidth: true
text: qsTr("Cancel")
enabled: logController.requestingList || logController.downloadingLogs
onClicked: logController.cancel()
}
}
}
Expand Down
Loading

0 comments on commit d0f173a

Please sign in to comment.