Commit dfbaf7f2 authored by Gus Grubba's avatar Gus Grubba

Core Mavlink log handler.

parent 42f447cc
...@@ -334,6 +334,7 @@ HEADERS += \ ...@@ -334,6 +334,7 @@ HEADERS += \
src/uas/UAS.h \ src/uas/UAS.h \
src/uas/UASInterface.h \ src/uas/UASInterface.h \
src/uas/UASMessageHandler.h \ src/uas/UASMessageHandler.h \
src/uas/MavlinkLogManager.h \
src/ui/toolbar/MainToolBarController.h \ src/ui/toolbar/MainToolBarController.h \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \
src/AutoPilotPlugins/APM/APMAirframeLoader.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \
...@@ -498,6 +499,7 @@ SOURCES += \ ...@@ -498,6 +499,7 @@ SOURCES += \
src/QmlControls/QmlObjectListModel.cc \ src/QmlControls/QmlObjectListModel.cc \
src/uas/UAS.cc \ src/uas/UAS.cc \
src/uas/UASMessageHandler.cc \ src/uas/UASMessageHandler.cc \
src/uas/MavlinkLogManager.cc \
src/ui/toolbar/MainToolBarController.cc \ src/ui/toolbar/MainToolBarController.cc \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \
src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ src/AutoPilotPlugins/APM/APMAirframeLoader.cc \
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "FollowMe.h" #include "FollowMe.h"
#include "PositionManager.h" #include "PositionManager.h"
#include "VideoManager.h" #include "VideoManager.h"
#include "MavlinkLogManager.h"
QGCToolbox::QGCToolbox(QGCApplication* app) QGCToolbox::QGCToolbox(QGCApplication* app)
: _audioOutput(NULL) : _audioOutput(NULL)
...@@ -50,6 +51,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) ...@@ -50,6 +51,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app)
, _followMe(NULL) , _followMe(NULL)
, _qgcPositionManager(NULL) , _qgcPositionManager(NULL)
, _videoManager(NULL) , _videoManager(NULL)
, _mavlinkLogManager(NULL)
{ {
_audioOutput = new GAudioOutput(app); _audioOutput = new GAudioOutput(app);
_autopilotPluginManager = new AutoPilotPluginManager(app); _autopilotPluginManager = new AutoPilotPluginManager(app);
...@@ -71,6 +73,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) ...@@ -71,6 +73,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app)
_qgcPositionManager = new QGCPositionManager(app); _qgcPositionManager = new QGCPositionManager(app);
_followMe = new FollowMe(app); _followMe = new FollowMe(app);
_videoManager = new VideoManager(app); _videoManager = new VideoManager(app);
_mavlinkLogManager = new MavlinkLogManager(app);
} }
void QGCToolbox::setChildToolboxes(void) void QGCToolbox::setChildToolboxes(void)
...@@ -95,11 +98,13 @@ void QGCToolbox::setChildToolboxes(void) ...@@ -95,11 +98,13 @@ void QGCToolbox::setChildToolboxes(void)
_followMe->setToolbox(this); _followMe->setToolbox(this);
_qgcPositionManager->setToolbox(this); _qgcPositionManager->setToolbox(this);
_videoManager->setToolbox(this); _videoManager->setToolbox(this);
_mavlinkLogManager->setToolbox(this);
} }
QGCToolbox::~QGCToolbox() QGCToolbox::~QGCToolbox()
{ {
delete _videoManager; delete _videoManager;
delete _mavlinkLogManager;
delete _audioOutput; delete _audioOutput;
delete _autopilotPluginManager; delete _autopilotPluginManager;
delete _factSystem; delete _factSystem;
......
...@@ -32,6 +32,7 @@ class QGCImageProvider; ...@@ -32,6 +32,7 @@ class QGCImageProvider;
class UASMessageHandler; class UASMessageHandler;
class QGCPositionManager; class QGCPositionManager;
class VideoManager; class VideoManager;
class MavlinkLogManager;
/// This is used to manage all of our top level services/tools /// This is used to manage all of our top level services/tools
class QGCToolbox { class QGCToolbox {
...@@ -56,6 +57,8 @@ public: ...@@ -56,6 +57,8 @@ public:
FollowMe* followMe(void) { return _followMe; } FollowMe* followMe(void) { return _followMe; }
QGCPositionManager* qgcPositionManager(void) { return _qgcPositionManager; } QGCPositionManager* qgcPositionManager(void) { return _qgcPositionManager; }
VideoManager* videoManager(void) { return _videoManager; } VideoManager* videoManager(void) { return _videoManager; }
MavlinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; }
#ifndef __mobile__ #ifndef __mobile__
GPSManager* gpsManager(void) { return _gpsManager; } GPSManager* gpsManager(void) { return _gpsManager; }
#endif #endif
...@@ -83,6 +86,7 @@ private: ...@@ -83,6 +86,7 @@ private:
FollowMe* _followMe; FollowMe* _followMe;
QGCPositionManager* _qgcPositionManager; QGCPositionManager* _qgcPositionManager;
VideoManager* _videoManager; VideoManager* _videoManager;
MavlinkLogManager* _mavlinkLogManager;
friend class QGCApplication; friend class QGCApplication;
}; };
......
...@@ -44,6 +44,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) ...@@ -44,6 +44,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app)
, _qgcPositionManager(NULL) , _qgcPositionManager(NULL)
, _missionCommandTree(NULL) , _missionCommandTree(NULL)
, _videoManager(NULL) , _videoManager(NULL)
, _mavlinkLogManager(NULL)
, _virtualTabletJoystick(false) , _virtualTabletJoystick(false)
, _baseFontPointSize(0.0) , _baseFontPointSize(0.0)
{ {
...@@ -60,7 +61,6 @@ QGroundControlQmlGlobal::~QGroundControlQmlGlobal() ...@@ -60,7 +61,6 @@ QGroundControlQmlGlobal::~QGroundControlQmlGlobal()
} }
void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox)
{ {
QGCTool::setToolbox(toolbox); QGCTool::setToolbox(toolbox);
...@@ -72,9 +72,9 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) ...@@ -72,9 +72,9 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox)
_qgcPositionManager = toolbox->qgcPositionManager(); _qgcPositionManager = toolbox->qgcPositionManager();
_missionCommandTree = toolbox->missionCommandTree(); _missionCommandTree = toolbox->missionCommandTree();
_videoManager = toolbox->videoManager(); _videoManager = toolbox->videoManager();
_mavlinkLogManager = toolbox->mavlinkLogManager();
} }
void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value)
{ {
QSettings settings; QSettings settings;
......
...@@ -72,6 +72,7 @@ public: ...@@ -72,6 +72,7 @@ public:
Q_PROPERTY(QGCPositionManager* qgcPositionManger READ qgcPositionManger CONSTANT) Q_PROPERTY(QGCPositionManager* qgcPositionManger READ qgcPositionManger CONSTANT)
Q_PROPERTY(MissionCommandTree* missionCommandTree READ missionCommandTree CONSTANT) Q_PROPERTY(MissionCommandTree* missionCommandTree READ missionCommandTree CONSTANT)
Q_PROPERTY(VideoManager* videoManager READ videoManager 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 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 Q_PROPERTY(qreal zOrderWidgets READ zOrderWidgets CONSTANT) ///< z order value to widgets, for example: zoom controls, hud widgetss
...@@ -166,6 +167,7 @@ public: ...@@ -166,6 +167,7 @@ public:
QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; } QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; }
MissionCommandTree* missionCommandTree () { return _missionCommandTree; } MissionCommandTree* missionCommandTree () { return _missionCommandTree; }
VideoManager* videoManager () { return _videoManager; } VideoManager* videoManager () { return _videoManager; }
MavlinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; }
qreal zOrderTopMost () { return 1000; } qreal zOrderTopMost () { return 1000; }
qreal zOrderWidgets () { return 100; } qreal zOrderWidgets () { return 100; }
...@@ -237,6 +239,7 @@ private: ...@@ -237,6 +239,7 @@ private:
QGCPositionManager* _qgcPositionManager; QGCPositionManager* _qgcPositionManager;
MissionCommandTree* _missionCommandTree; MissionCommandTree* _missionCommandTree;
VideoManager* _videoManager; VideoManager* _videoManager;
MavlinkLogManager* _mavlinkLogManager;
bool _virtualTabletJoystick; bool _virtualTabletJoystick;
qreal _baseFontPointSize; qreal _baseFontPointSize;
......
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* 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 <QQmlContext>
#include <QQmlProperty>
#include <QQmlEngine>
#include <QtQml>
#include <QSettings>
#include <QHttpPart>
#include <QNetworkReply>
#include <QFile>
#include <QFileInfo>
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<MavlinkLogManager>("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<MavlinkLogFiles*>(_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<MavlinkLogFiles*>(_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<QNetworkReply*>(sender());
if(!reply) {
return;
}
QByteArray data = reply->readAll();
qCDebug(MavlinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data);
}
//-----------------------------------------------------------------------------
void
MavlinkLogManager::_uploadFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(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;
}
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* 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 <QObject>
#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
...@@ -25,8 +25,36 @@ Rectangle { ...@@ -25,8 +25,36 @@ Rectangle {
color: qgcPal.window color: qgcPal.window
anchors.fill: parent anchors.fill: parent
property real _labelWidth: ScreenTools.defaultFontPixelWidth * 28
property real _valueWidth: ScreenTools.defaultFontPixelWidth * 24
property int _selectedCount: 0
QGCPalette { id: qgcPal } 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 { QGCFlickable {
clip: true clip: true
anchors.fill: parent anchors.fill: parent
...@@ -36,45 +64,319 @@ Rectangle { ...@@ -36,45 +64,319 @@ Rectangle {
Column { Column {
id: settingsColumn id: settingsColumn
spacing: ScreenTools.defaultFontPixelHeight width: __mavlinkRoot.width
spacing: ScreenTools.defaultFontPixelHeight * 0.5
anchors.margins: ScreenTools.defaultFontPixelWidth anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.left: parent.left
anchors.top: parent.top
//----------------------------------------------------------------- //-----------------------------------------------------------------
//-- System ID //-- Ground Station
Row { Item {
spacing: ScreenTools.defaultFontPixelWidth width: __mavlinkRoot.width * 0.8
height: gcsLabel.height
anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.horizontalCenter: parent.horizontalCenter
QGCLabel { QGCLabel {
text: qsTr("Ground Station MavLink System ID:") id: gcsLabel
anchors.verticalCenter: parent.verticalCenter text: qsTr("Ground Station")
font.family: ScreenTools.demiboldFontFamily
} }
QGCTextField { }
id: sysidField Rectangle {
text: QGroundControl.mavlinkSystemID.toString() height: gcsColumn.height + (ScreenTools.defaultFontPixelHeight * 2)
width: ScreenTools.defaultFontPixelWidth * 6 width: __mavlinkRoot.width * 0.8
inputMethodHints: Qt.ImhFormattedNumbersOnly color: qgcPal.windowShade
anchors.verticalCenter: parent.verticalCenter anchors.margins: ScreenTools.defaultFontPixelWidth
onEditingFinished: { anchors.horizontalCenter: parent.horizontalCenter
QGroundControl.mavlinkSystemID = parseInt(sysidField.text) 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 //-- Mavlink Logging
QGCCheckBox { Item {
text: qsTr("Emit heartbeat") width: __mavlinkRoot.width * 0.8
checked: QGroundControl.multiVehicleManager.gcsHeartBeatEnabled height: logLabel.height
onClicked: { anchors.margins: ScreenTools.defaultFontPixelWidth
QGroundControl.multiVehicleManager.gcsHeartBeatEnabled = checked 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 //-- Log Files
QGCCheckBox { Item {
text: qsTr("Only accept MAVs with same protocol version") width: __mavlinkRoot.width * 0.8
checked: QGroundControl.isVersionCheckEnabled height: logFilesLabel.height
onClicked: { anchors.margins: ScreenTools.defaultFontPixelWidth
QGroundControl.isVersionCheckEnabled = checked 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()
}
}
}
}
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment