diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 9dfad863f037d1c97aaa31f5a6a1e1ce778c5e52..c8adcb54b0ea42265db557a6a1d020301cd10971 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -334,6 +334,7 @@ HEADERS += \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ + src/uas/MavlinkLogManager.h \ src/ui/toolbar/MainToolBarController.h \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ @@ -498,6 +499,7 @@ SOURCES += \ src/QmlControls/QmlObjectListModel.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ + src/uas/MavlinkLogManager.cc \ src/ui/toolbar/MainToolBarController.cc \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 5c63154bed9d133066c588a4cf4f52803b46efe1..fd569b7b5abd236a377f744d1a6ae3f540640ad4 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -28,6 +28,7 @@ #include "FollowMe.h" #include "PositionManager.h" #include "VideoManager.h" +#include "MavlinkLogManager.h" QGCToolbox::QGCToolbox(QGCApplication* app) : _audioOutput(NULL) @@ -50,6 +51,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) , _followMe(NULL) , _qgcPositionManager(NULL) , _videoManager(NULL) + , _mavlinkLogManager(NULL) { _audioOutput = new GAudioOutput(app); _autopilotPluginManager = new AutoPilotPluginManager(app); @@ -71,6 +73,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) _qgcPositionManager = new QGCPositionManager(app); _followMe = new FollowMe(app); _videoManager = new VideoManager(app); + _mavlinkLogManager = new MavlinkLogManager(app); } void QGCToolbox::setChildToolboxes(void) @@ -95,11 +98,13 @@ void QGCToolbox::setChildToolboxes(void) _followMe->setToolbox(this); _qgcPositionManager->setToolbox(this); _videoManager->setToolbox(this); + _mavlinkLogManager->setToolbox(this); } QGCToolbox::~QGCToolbox() { delete _videoManager; + delete _mavlinkLogManager; delete _audioOutput; delete _autopilotPluginManager; delete _factSystem; diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 540c917f3b268bdba33c886418ae324ee6df42b9..1b0080de5f95052fc00a91fc421517bddc5a2ff5 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -32,6 +32,7 @@ class QGCImageProvider; class UASMessageHandler; class QGCPositionManager; class VideoManager; +class MavlinkLogManager; /// This is used to manage all of our top level services/tools class QGCToolbox { @@ -56,6 +57,8 @@ public: FollowMe* followMe(void) { return _followMe; } QGCPositionManager* qgcPositionManager(void) { return _qgcPositionManager; } VideoManager* videoManager(void) { return _videoManager; } + MavlinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; } + #ifndef __mobile__ GPSManager* gpsManager(void) { return _gpsManager; } #endif @@ -83,6 +86,7 @@ private: FollowMe* _followMe; QGCPositionManager* _qgcPositionManager; VideoManager* _videoManager; + MavlinkLogManager* _mavlinkLogManager; friend class QGCApplication; }; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index abd618bc3897b52f0bf93d8c893255da351a0ea2..c249191bdba2e3cbd4f844c03bf979b098c21964 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -44,6 +44,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _qgcPositionManager(NULL) , _missionCommandTree(NULL) , _videoManager(NULL) + , _mavlinkLogManager(NULL) , _virtualTabletJoystick(false) , _baseFontPointSize(0.0) { @@ -60,7 +61,6 @@ QGroundControlQmlGlobal::~QGroundControlQmlGlobal() } - void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); @@ -72,9 +72,9 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _qgcPositionManager = toolbox->qgcPositionManager(); _missionCommandTree = toolbox->missionCommandTree(); _videoManager = toolbox->videoManager(); + _mavlinkLogManager = toolbox->mavlinkLogManager(); } - void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) { QSettings settings; diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 69180218490dda7bbdcb5a6de89eadafc3d14f48..b23823f44852da08b340dfa3edd61746c055b337 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -72,6 +72,7 @@ public: Q_PROPERTY(QGCPositionManager* qgcPositionManger READ qgcPositionManger CONSTANT) Q_PROPERTY(MissionCommandTree* missionCommandTree READ missionCommandTree CONSTANT) Q_PROPERTY(VideoManager* videoManager READ videoManager CONSTANT) + Q_PROPERTY(MavlinkLogManager* mavlinkLogManager READ mavlinkLogManager CONSTANT) Q_PROPERTY(qreal zOrderTopMost READ zOrderTopMost CONSTANT) ///< z order for top most items, toolbar, main window sub view Q_PROPERTY(qreal zOrderWidgets READ zOrderWidgets CONSTANT) ///< z order value to widgets, for example: zoom controls, hud widgetss @@ -166,6 +167,7 @@ public: QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; } MissionCommandTree* missionCommandTree () { return _missionCommandTree; } VideoManager* videoManager () { return _videoManager; } + MavlinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } @@ -237,6 +239,7 @@ private: QGCPositionManager* _qgcPositionManager; MissionCommandTree* _missionCommandTree; VideoManager* _videoManager; + MavlinkLogManager* _mavlinkLogManager; bool _virtualTabletJoystick; qreal _baseFontPointSize; diff --git a/src/uas/MavlinkLogManager.cc b/src/uas/MavlinkLogManager.cc new file mode 100644 index 0000000000000000000000000000000000000000..2b45f8a231008c0613f01bb6991bff56e90520b5 --- /dev/null +++ b/src/uas/MavlinkLogManager.cc @@ -0,0 +1,349 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "MavlinkLogManager.h" +#include "QGCApplication.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QGC_LOGGING_CATEGORY(MavlinkLogManagerLog, "MavlinkLogManagerLog") + +static const char* kEmailAddressKey = "MavlinkLogEmail"; +static const char* kDescriptionsKey = "MavlinkLogDescription"; +static const char* kDefaultDescr = "QGroundControl Session"; +static const char* kPx4URLKey = "MavlinkLogURL"; +static const char* kDefaultPx4URL = "http://logs.px4.io/upload"; +static const char* kEnableAutologKey= "EnableAutologKey"; + + +//----------------------------------------------------------------------------- +MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager *manager, const QString& filePath) + : _manager(manager) + , _size(0) + , _selected(false) + , _uploading(false) + , _progress(0) +{ + QFileInfo fi(filePath); + _name = fi.baseName(); + _size = (quint32)fi.size(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setSelected(bool selected) +{ + _selected = selected; + emit selectedChanged(); + emit _manager->selectedCountChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setUploading(bool uploading) +{ + _uploading = uploading; + emit uploadingChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setProgress(qreal progress) +{ + _progress = progress; + emit progressChanged(); +} + +//----------------------------------------------------------------------------- +MavlinkLogManager::MavlinkLogManager(QGCApplication* app) + : QGCTool(app) + , _enableAutolog(true) + , _nam(NULL) + , _currentLogfile(NULL) +{ + //-- Get saved settings + QSettings settings; + setEmailAddress(settings.value(kEmailAddressKey, QString()).toString()); + setDescription(settings.value(kDescriptionsKey, QString(kDefaultDescr)).toString()); + setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString()); + setEnableAutolog(settings.value(kEnableAutologKey, true).toBool()); + //-- Logging location + _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + _logPath += "/MavlinkLogs"; + if(!QDir(_logPath).exists()) { + if(QDir().mkpath(_logPath)) { + qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log download path:" << _logPath; + } + } + //-- Load current list of logs + QDirIterator it(_logPath, QStringList() << "*.ulg", QDir::Files); + while(it.hasNext()) { + _logFiles.append(new MavlinkLogFiles(this, it.next())); + } + qCDebug(MavlinkLogManagerLog) << "Mavlink logs directory:" << _logPath; +} + +//----------------------------------------------------------------------------- +MavlinkLogManager::~MavlinkLogManager() +{ + _logFiles.clear(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); + + // _uploadURL = "http://192.168.1.21/px4"; + // _uploadURL = "http://192.168.1.9:8080"; + // _emailAddress = "gus.grubba.com"; + // _description = "Test from QGroundControl - Discard"; + // _sendLog("/Users/gus/github/work/logs/simulator.ulg"); + +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setEmailAddress(QString email) +{ + _emailAddress = email; + QSettings settings; + settings.setValue(kEmailAddressKey, email); + emit emailAddressChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setDescription(QString description) +{ + _description = description; + QSettings settings; + settings.setValue(kDescriptionsKey, description); + emit descriptionChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setUploadURL(QString url) +{ + _uploadURL = url; + if(_uploadURL.isEmpty()) { + _uploadURL = kDefaultPx4URL; + } + QSettings settings; + settings.setValue(kPx4URLKey, _uploadURL); + emit uploadURLChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setEnableAutolog(bool enable) +{ + _enableAutolog = enable; + QSettings settings; + settings.setValue(kEnableAutologKey, enable); + emit enableAutologChanged(); +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogManager::busy() +{ + return _currentLogfile != NULL; +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::uploadLog() +{ + if(_currentLogfile) { + _currentLogfile->setUploading(false); + } + for(int i = 0; i < _logFiles.count(); i++ ) { + _currentLogfile = qobject_cast(_logFiles.get(i)); + Q_ASSERT(_currentLogfile); + if(_currentLogfile->selected()) { + _currentLogfile->setSelected(false); + _currentLogfile->setUploading(true); + _currentLogfile->setProgress(0.0); + QString filePath = _logPath; + filePath += "/"; + filePath += _currentLogfile->name(); + filePath += ".ulg"; + _sendLog(filePath); + emit busyChanged(); + return; + } + } + _currentLogfile = NULL; + emit busyChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::deleteLog() +{ + //-- TODO +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::cancelUpload() +{ + for(int i = 0; i < _logFiles.count(); i++ ) { + MavlinkLogFiles* pLogFile = qobject_cast(_logFiles.get(i)); + Q_ASSERT(pLogFile); + if(pLogFile->selected() && pLogFile != _currentLogfile) { + pLogFile->setSelected(false); + } + } + if(_currentLogfile) { + emit abortUpload(); + } +} + +//----------------------------------------------------------------------------- +QHttpPart +create_form_part(const QString& name, const QString& value) +{ + QHttpPart formPart; + formPart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(name)); + formPart.setBody(value.toUtf8()); + return formPart; +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogManager::_sendLog(const QString& logFile) +{ + QString defaultDescription = _description; + if(_description.isEmpty()) { + qCWarning(MavlinkLogManagerLog) << "Log description missing. Using defaults."; + defaultDescription = kDefaultDescr; + } + if(_emailAddress.isEmpty()) { + qCCritical(MavlinkLogManagerLog) << "User email missing."; + return false; + } + if(_uploadURL.isEmpty()) { + qCCritical(MavlinkLogManagerLog) << "Upload URL missing."; + return false; + } + QFileInfo fi(logFile); + if(!fi.exists()) { + qCCritical(MavlinkLogManagerLog) << "Log file missing:" << logFile; + return false; + } + QFile *file = new QFile(logFile); + if(!file || !file->open(QIODevice::ReadOnly)) { + if (file) delete file; + qCCritical(MavlinkLogManagerLog) << "Could not open log file:" << logFile; + return false; + } + if(!_nam) { + _nam = new QNetworkAccessManager(this); + } + QNetworkProxy savedProxy = _nam->proxy(); + QNetworkProxy tempProxy; + tempProxy.setType(QNetworkProxy::DefaultProxy); + _nam->setProxy(tempProxy); + //-- Build POST request + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpPart emailPart = create_form_part("email", _emailAddress); + QHttpPart descriptionPart = create_form_part("description", _description); + QHttpPart sourcePart = create_form_part("source", "QGroundControl"); + QHttpPart versionPart = create_form_part("version", _app->applicationVersion()); + QHttpPart logPart; + logPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream"); + logPart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"filearg\"; filename=\"%1\"").arg(fi.fileName())); + logPart.setBodyDevice(file); + //-- Assemble request and POST it + multiPart->append(emailPart); + multiPart->append(descriptionPart); + multiPart->append(sourcePart); + multiPart->append(versionPart); + multiPart->append(logPart); + file->setParent(multiPart); + QNetworkRequest request(_uploadURL); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReply *reply = _nam->post(request, multiPart); + connect(reply, &QNetworkReply::finished, this, &MavlinkLogManager::_uploadFinished); + connect(this, &MavlinkLogManager::abortUpload, reply, &QNetworkReply::abort); + //connect(reply, &QNetworkReply::readyRead, this, &MavlinkLogManager::_dataAvailable); + connect(reply, &QNetworkReply::uploadProgress, this, &MavlinkLogManager::_uploadProgress); + multiPart->setParent(reply); + qCDebug(MavlinkLogManagerLog) << "Log" << fi.baseName() << "Uploading." << fi.size() << "bytes."; + _nam->setProxy(savedProxy); + return true; +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogManager::_processUploadResponse(int http_code, QByteArray &data) +{ + qCDebug(MavlinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data); + emit readyRead(data); + return http_code == 200; +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_dataAvailable() +{ + QNetworkReply *reply = qobject_cast(sender()); + if(!reply) { + return; + } + QByteArray data = reply->readAll(); + qCDebug(MavlinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_uploadFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + if(!reply) { + return; + } + const int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QByteArray data = reply->readAll(); + if(_processUploadResponse(http_code, data)) { + qCDebug(MavlinkLogManagerLog) << "Log uploaded."; + emit succeed(); + } else { + qCDebug(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); + emit failed(); + } + reply->deleteLater(); + //-- Next (if any) + uploadLog(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) +{ + if(bytesTotal) { + qreal progress = (qreal)bytesSent / (qreal)bytesTotal; + if(_currentLogfile) + _currentLogfile->setProgress(progress); + } + qCDebug(MavlinkLogManagerLog) << bytesSent << "of" << bytesTotal; +} diff --git a/src/uas/MavlinkLogManager.h b/src/uas/MavlinkLogManager.h new file mode 100644 index 0000000000000000000000000000000000000000..de41bc1ef6317b784575a416a771fe0df1dccd66 --- /dev/null +++ b/src/uas/MavlinkLogManager.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + + +#ifndef MavlinkLogManager_H +#define MavlinkLogManager_H + +#include + +#include "QmlObjectListModel.h" +#include "QGCLoggingCategory.h" +#include "QGCToolbox.h" + +Q_DECLARE_LOGGING_CATEGORY(MavlinkLogManagerLog) + +class QNetworkAccessManager; +class MavlinkLogManager; + +//----------------------------------------------------------------------------- +class MavlinkLogFiles : public QObject +{ + Q_OBJECT +public: + MavlinkLogFiles (MavlinkLogManager* manager, const QString& filePath); + + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(quint32 size READ size CONSTANT) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) + Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + + QString name () { return _name; } + quint32 size () { return _size; } + bool selected () { return _selected; } + bool uploading () { return _uploading; } + qreal progress () { return _progress; } + + void setSelected (bool selected); + void setUploading (bool uploading); + void setProgress (qreal progress); + +signals: + void selectedChanged (); + void uploadingChanged (); + void progressChanged (); + +private: + MavlinkLogManager* _manager; + QString _name; + quint32 _size; + bool _selected; + bool _uploading; + qreal _progress; +}; + +class MavlinkLogManager : public QGCTool +{ + Q_OBJECT + +public: + MavlinkLogManager (QGCApplication* app); + ~MavlinkLogManager (); + + Q_PROPERTY(QString emailAddress READ emailAddress WRITE setEmailAddress NOTIFY emailAddressChanged) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QString uploadURL READ uploadURL WRITE setUploadURL NOTIFY uploadURLChanged) + Q_PROPERTY(bool enableAutolog READ enableAutolog WRITE setEnableAutolog NOTIFY enableAutologChanged) + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + Q_PROPERTY(QmlObjectListModel* logFiles READ logFiles NOTIFY logFilesChanged) + + Q_INVOKABLE void uploadLog (); + Q_INVOKABLE void deleteLog (); + Q_INVOKABLE void cancelUpload (); + + QString emailAddress () { return _emailAddress; } + QString description () { return _description; } + QString uploadURL () { return _uploadURL; } + bool enableAutolog () { return _enableAutolog; } + bool busy (); + + QmlObjectListModel* logFiles () { return &_logFiles; } + + void setEmailAddress (QString email); + void setDescription (QString description); + void setUploadURL (QString url); + void setEnableAutolog (bool enable); + + // Override from QGCTool + void setToolbox (QGCToolbox *toolbox); + +signals: + void emailAddressChanged (); + void descriptionChanged (); + void uploadURLChanged (); + void enableAutologChanged (); + void logFilesChanged (); + void selectedCountChanged (); + void busyChanged (); + void readyRead (QByteArray data); + void failed (); + void succeed (); + void abortUpload (); + +private slots: + void _uploadFinished (); + void _dataAvailable (); + void _uploadProgress (qint64 bytesSent, qint64 bytesTotal); + +private: + bool _sendLog (const QString& logFile); + bool _processUploadResponse (int http_code, QByteArray &data); + +private: + QString _description; + QString _emailAddress; + QString _uploadURL; + QString _logPath; + bool _enableAutolog; + QNetworkAccessManager* _nam; + QmlObjectListModel _logFiles; + MavlinkLogFiles* _currentLogfile; +}; + +#endif diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 37ac33284738907b82949d711ebc5f5686f32259..9f0cf1e4007e16daa0c887eeef2214c4da3d614e 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -25,8 +25,36 @@ Rectangle { color: qgcPal.window anchors.fill: parent + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 28 + property real _valueWidth: ScreenTools.defaultFontPixelWidth * 24 + property int _selectedCount: 0 + QGCPalette { id: qgcPal } + Connections { + target: QGroundControl.mavlinkLogManager + onSelectedCountChanged: { + var selected = 0 + for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { + var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) + console.log(logFile.selected) + if(logFile.selected) + selected++ + } + _selectedCount = selected + console.log(_selectedCount) + } + } + + MessageDialog { + id: emptyEmailDialog + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Close + title: qsTr("Uploading Log Files") + text: qsTr("Please enter an email address before uploading log files.") + } + QGCFlickable { clip: true anchors.fill: parent @@ -36,45 +64,319 @@ Rectangle { Column { id: settingsColumn - spacing: ScreenTools.defaultFontPixelHeight + width: __mavlinkRoot.width + spacing: ScreenTools.defaultFontPixelHeight * 0.5 anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.left: parent.left - anchors.top: parent.top //----------------------------------------------------------------- - //-- System ID - Row { - spacing: ScreenTools.defaultFontPixelWidth + //-- Ground Station + Item { + width: __mavlinkRoot.width * 0.8 + height: gcsLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCLabel { - text: qsTr("Ground Station MavLink System ID:") - anchors.verticalCenter: parent.verticalCenter + id: gcsLabel + text: qsTr("Ground Station") + font.family: ScreenTools.demiboldFontFamily } - QGCTextField { - id: sysidField - text: QGroundControl.mavlinkSystemID.toString() - width: ScreenTools.defaultFontPixelWidth * 6 - inputMethodHints: Qt.ImhFormattedNumbersOnly - anchors.verticalCenter: parent.verticalCenter - onEditingFinished: { - QGroundControl.mavlinkSystemID = parseInt(sysidField.text) + } + Rectangle { + height: gcsColumn.height + (ScreenTools.defaultFontPixelHeight * 2) + width: __mavlinkRoot.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: gcsColumn + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: sysidField.baseline + text: qsTr("MavLink System ID:") + } + QGCTextField { + id: sysidField + text: QGroundControl.mavlinkSystemID.toString() + width: _valueWidth + inputMethodHints: Qt.ImhFormattedNumbersOnly + anchors.verticalCenter: parent.verticalCenter + onEditingFinished: { + QGroundControl.mavlinkSystemID = parseInt(sysidField.text) + } + } + } + //----------------------------------------------------------------- + //-- Mavlink Heartbeats + QGCCheckBox { + text: qsTr("Emit heartbeat") + checked: QGroundControl.multiVehicleManager.gcsHeartBeatEnabled + onClicked: { + QGroundControl.multiVehicleManager.gcsHeartBeatEnabled = checked + } + } + //----------------------------------------------------------------- + //-- Mavlink Version Check + QGCCheckBox { + text: qsTr("Only accept MAVs with same protocol version") + checked: QGroundControl.isVersionCheckEnabled + onClicked: { + QGroundControl.isVersionCheckEnabled = checked + } } } } //----------------------------------------------------------------- - //-- Mavlink Heartbeats - QGCCheckBox { - text: qsTr("Emit heartbeat") - checked: QGroundControl.multiVehicleManager.gcsHeartBeatEnabled - onClicked: { - QGroundControl.multiVehicleManager.gcsHeartBeatEnabled = checked + //-- Mavlink Logging + Item { + width: __mavlinkRoot.width * 0.8 + height: logLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: logLabel + text: qsTr("Vehicle Mavlink Logging") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: logColumn.height + (ScreenTools.defaultFontPixelHeight * 2) + width: __mavlinkRoot.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: logColumn + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + //----------------------------------------------------------------- + //-- Email address Field + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: emailField.baseline + text: qsTr("Email address for Log Upload:") + } + QGCTextField { + id: emailField + text: QGroundControl.mavlinkLogManager.emailAddress + width: _valueWidth + inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhEmailCharactersOnly + anchors.verticalCenter: parent.verticalCenter + onEditingFinished: { + QGroundControl.mavlinkLogManager.emailAddress = emailField.text + } + } + } + //----------------------------------------------------------------- + //-- Description Field + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: descField.baseline + text: qsTr("Default Description:") + } + QGCTextField { + id: descField + text: QGroundControl.mavlinkLogManager.description + width: _valueWidth + anchors.verticalCenter: parent.verticalCenter + onEditingFinished: { + QGroundControl.mavlinkLogManager.description = descField.text + } + } + } + //----------------------------------------------------------------- + //-- Upload URL + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: urlField.baseline + text: qsTr("Default Upload URL") + } + QGCTextField { + id: urlField + text: QGroundControl.mavlinkLogManager.uploadURL + width: _valueWidth + inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly + anchors.verticalCenter: parent.verticalCenter + onEditingFinished: { + QGroundControl.mavlinkLogManager.uploadURL = urlField.text + } + } + } + //----------------------------------------------------------------- + //-- Automatic Upload + QGCCheckBox { + text: qsTr("Enable automatic log uploads") + checked: QGroundControl.mavlinkLogManager.enableAutolog + enabled: emailField.text !== "" && urlField !== "" + onClicked: { + QGroundControl.mavlinkLogManager.enableAutolog = checked + } + } } } //----------------------------------------------------------------- - //-- Mavlink Version Check - QGCCheckBox { - text: qsTr("Only accept MAVs with same protocol version") - checked: QGroundControl.isVersionCheckEnabled - onClicked: { - QGroundControl.isVersionCheckEnabled = checked + //-- Log Files + Item { + width: __mavlinkRoot.width * 0.8 + height: logFilesLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: logFilesLabel + text: qsTr("Saved Log Files") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: logFilesColumn.height + (ScreenTools.defaultFontPixelHeight * 2) + width: __mavlinkRoot.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: logFilesColumn + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + Rectangle { + width: ScreenTools.defaultFontPixelWidth * 52 + height: ScreenTools.defaultFontPixelHeight * 10 + anchors.horizontalCenter: parent.horizontalCenter + color: qgcPal.windowShade + border.color: qgcPal.text + border.width: 0.5 + ListView { + width: ScreenTools.defaultFontPixelWidth * 50 + height: ScreenTools.defaultFontPixelHeight * 9 + anchors.centerIn: parent + orientation: ListView.Vertical + model: QGroundControl.mavlinkLogManager.logFiles + delegate: Rectangle { + width: ScreenTools.defaultFontPixelWidth * 48 + height: ScreenTools.defaultFontPixelHeight * 1.25 + color: index % 2 == 0 ? qgcPal.window : qgcPal.windowShade + anchors.horizontalCenter: parent.horizontalCenter; + Row { + width: ScreenTools.defaultFontPixelWidth * 46 + anchors.centerIn: parent + spacing: ScreenTools.defaultFontPixelWidth + QGCCheckBox { + width: ScreenTools.defaultFontPixelWidth * 4 + checked: object.selected + onClicked: { + object.selected = checked + } + } + QGCLabel { + text: object.name + width: ScreenTools.defaultFontPixelWidth * 20 + } + QGCLabel { + text: object.size + visible: !object.uploading + width: ScreenTools.defaultFontPixelWidth * 20; + horizontalAlignment: Text.AlignRight + } + ProgressBar { + visible: object.uploading + width: ScreenTools.defaultFontPixelWidth * 20; + height: ScreenTools.defaultFontPixelHeight + anchors.verticalCenter: parent.verticalCenter + minimumValue: 0 + maximumValue: 100 + value: object.progress * 100.0 + } + } + } + } + } + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCButton { + text: "Check All" + enabled: !QGroundControl.mavlinkLogManager.busy + onClicked: { + for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { + var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) + logFile.selected = true + } + } + } + QGCButton { + text: "Check None" + enabled: !QGroundControl.mavlinkLogManager.busy + onClicked: { + for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { + var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) + logFile.selected = false + } + } + } + QGCButton { + text: "Delete Selected" + enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.busy + onClicked: deleteDialog.open() + MessageDialog { + id: deleteDialog + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Delete Selected Log Files") + text: qsTr("Confirm deleting selected log files?") + onYes: { + QGroundControl.mavlinkLogManager.deleteLog() + } + } + } + QGCButton { + text: "Upload Selected" + enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.busy + visible: !QGroundControl.mavlinkLogManager.busy + onClicked: { + QGroundControl.mavlinkLogManager.emailAddress = emailField.text + if(QGroundControl.mavlinkLogManager.emailAddress === "") + emptyEmailDialog.open() + else + uploadDialog.open() + } + MessageDialog { + id: uploadDialog + visible: false + icon: StandardIcon.Question + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Upload Selected Log Files") + text: qsTr("Confirm uploading selected log files?") + onYes: { + QGroundControl.mavlinkLogManager.uploadLog() + } + } + } + QGCButton { + text: "Cancel" + enabled: QGroundControl.mavlinkLogManager.busy + visible: QGroundControl.mavlinkLogManager.busy + onClicked: cancelDialog.open() + MessageDialog { + id: cancelDialog + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Cancel Upload") + text: qsTr("Confirm canceling the upload process?") + onYes: { + QGroundControl.mavlinkLogManager.cancelUpload() + } + } + } + } } } }